Code Smell in Mobile Development: What It Is, Types, and Fixing Principles

Author: IT Sectr Published: 2026-05-13 Reading time: 9 min

Code Smell is a surface-level characteristic in code that signals a potential problem in the design or architecture of an application. The term was coined by Kent Beck and popularized by Martin Fowler in the book “Refactoring: Improving the Design of Existing Code”. According to Martin Fowler, a code smell does not necessarily mean a bug, but almost always indicates the need for refactoring to improve maintainability.

Key Takeaways

  • Code Smell — a surface indicator of a code problem that is not an error but complicates maintenance and development
  • Long Method — the most common smell: a method that does too much and needs to be split into several
  • Large Class — a class that violates the Single Responsibility Principle and contains logic from different domains
  • Duplicate Code — repeated code fragments that require fixing in multiple places when changed
  • Feature Envy — a method that uses data from another class more than from its own

What Is Code Smell

Code Smell is a metaphor for symptoms in source code that with high probability indicate deeper problems. The term itself has no formal definition — it is a heuristic based on developers’ experience. Martin Fowler and Kent Beck first systematized 22 smells in 1999 in the book “Refactoring”, and most of them remain relevant decades later.

It is important to understand the difference between Code Smell and a bug. A smell is not an error: the code compiles, works, and produces correct results. The problem is that such code is hard to read, change, and test. Over time, the cost of each change increases, and confidence in the correctness of refactoring decreases. Static analysis tools (SonarQube, Detekt, SwiftLint) automatically detect many smells.

The heuristic nature of Code Smell means that not every long method needs to be split, and not every large class requires refactoring. The decision is made by the developer, evaluating the context: frequency of changes, module criticality, development plans. Experienced engineers sense a smell intuitively — the code “smells bad” even though all formal rules are followed.

Main Types of Code Smell

Fowler identified 22 smells grouped into several categories. For mobile development, the most relevant are structural smells, object-oriented design smells, and specific problems related to platform constraints. Let us examine each group with real-world examples.

Structural Smells

Long Method is the most common smell in mobile applications. A registration form screen often contains a single setupUI method that is 200+ lines long, creating all Views, setting up constraints, subscribing to events, and handling errors. Solution: break it down into methods by logical blocks — configureEmailField, configurePasswordField, setupConstraints, bindViewModel.

Large Class — an Activity or ViewController responsible for display, navigation, business logic, and network interactions all at once. Such a class violates the Single Responsibility Principle and contains dozens of fields and methods. In Android, this is often a Fragment with 1000+ lines containing logic from different screens. Solution: extract a presenter/ViewModel, move network code to a repository, and navigation to a coordinator.

Duplicate Code — copying identical blocks in different parts of the application. A typical example: two screens displaying a product card — in the catalog and in favorites. If the display logic is copied, fixing a bug in one place will not fix it in another. Solution: extract common logic into a reusable component or extension.

Object-Oriented Design Smells

Feature Envy — a method of one class intensively uses data from another class. In Android, this manifests when a ViewModel directly accesses fields of a User model instead of calling a model method. Signal: if a method can be moved to the class whose data it uses — move it. Switch Statements (condition chains) — a switch construct or if-else chain checking the object type. Instead, use polymorphism or the strategy pattern.

Data Class — a class that only stores data but contains no behavior. Data classes in Kotlin or structs in Swift are not inherently a smell. The problem arises when business logic working with that data is scattered across the codebase instead of being encapsulated. Refused Bequest — a subclass does not use most of the parent’s methods and overrides them with empty stubs. A sign of incorrect inheritance: replace inheritance with composition.

Smells in Mobile Development

God Activity / God Fragment — an Activity or Fragment that knows everything: lifecycle, data, navigation, Permissions, DI. This is the most expensive class to maintain in an application. Solution: architectural patterns MVVM, MVI, or Clean Architecture separate responsibilities. Giant ViewController — the iOS equivalent, where a UIViewController contains all the screen logic and often exceeds 500 lines.

Hardcoded Resources — strings, colors, sizes, API URLs embedded directly into code. In Android, this violates the R resource system; in iOS, NSLocalizedString and Asset Catalog. Fix: move all strings to strings.xml or Localizable.strings, URLs to a config file, sizes to dimens. Leaking Context — holding a reference to an Activity or ViewController longer than the component itself lives. Leads to memory leaks and crashes. Solution: weak references, Jetpack Lifecycle, RxSwift DisposeBag.

SmellWhere It OccursSolution
Long MethodAndroid/iOSExtract Method, split up
Large ClassActivity, ViewControllerMVVM, VIPER, Clean Arch
Duplicate CodeAny screensShared Component, DRY
Feature EnvyViewModel, PresenterMove Method
Leaking ContextAndroidLifecycle-aware components

How to Find Code Smell

Code review is the most reliable way to detect smells. The human eye notices unnatural constructs that automated analyzers miss. Code review effectiveness improves when the team uses a checklist of typical smells. It is recommended to review no more than 200–400 lines of code per session — after this threshold, attention drops and smells start slipping through.

Static analysis automates the search for structural smells. For Android, the standard tools are Detekt (Kotlin) and Android Lint; for iOS, SwiftLint and SonarQube. These tools find long methods, large classes, duplicate code, and many other issues. It is important to tune rules for the project — default configurations are often too strict or, on the contrary, miss critical smells.

Code metrics provide objective criteria: Cyclomatic Complexity (threshold >10 requires attention), Lines of Code per Method (threshold >30), Depth of Inheritance (>3 — reason to think). Tools like CodeMetrics (Xcode) and the Gradle Metrics Plugin build graphs of metric changes over time. If a method’s complexity grew from 5 to 15 after the last commit — that is a signal for refactoring.

kotlin
// Example: method with Cyclomatic complexity = 7 (above threshold 5)
fun processOrder(order: Order) {
    if (order.status == Status.NEW) { /* 10 lines */ }
    else if (order.status == Status.PAID) { /* 15 lines */ }
    else if (order.status == Status.SHIPPED) { /* 20 lines */ }
    else if (order.status == Status.DELIVERED) { /* 8 lines */ }
    else if (order.status == Status.CANCELLED) { /* 5 lines */ }
    else { throw IllegalStateException() }
}

// Fix: polymorphism instead of switch
interface OrderHandler {
    fun handle(order: Order)
}

Automated smell detection does not replace code review: static analyzers only find structural problems but do not catch semantic smells (Feature Envy, Inappropriate Intimacy). A combination of automated tools and human review yields the best results. Configure your CI/CD pipeline so that builds fail when complexity or method length thresholds are exceeded.

How to Fix Code Smell

Refactoring is the primary method for eliminating code smells. Fowler describes dozens of refactoring techniques, each applicable to a specific smell. Extract Method — for long methods, Extract Class — for large classes, Move Method — for Feature Envy. It is important to perform refactoring in small steps, keeping the code working after each change.

Tests before refactoring are mandatory. If the code is not covered by unit tests, refactoring turns into rewriting with unknown results. For legacy code without tests, use Characterization Tests — write tests that capture the current behavior, then refactor. Testing gives confidence that business logic has not broken after refactoring.

Gradualness is the key to successfully fixing smells in mobile development. Do not try to rewrite a God Activity entirely. First extract the navigation layer, then the data layer, then the display logic. Each step should be accompanied by a commit and test run. Use feature toggles to enable the refactoring for a subset of users and roll back if problems arise.

  • Extract Method — split a long method into several short ones with clear names
  • Extract Class — extract a related group of fields and methods into a separate class
  • Replace Conditional with Polymorphism — replace switch with a class hierarchy
  • Introduce Parameter Object — combine a group of parameters into an object
  • Replace Inheritance with Delegation — replace extends with composition

IDE tools automate many refactoring techniques. Android Studio and IntelliJ IDEA offer built-in refactorings: Extract Method, Extract Interface, Pull Members Up, Encapsulate Fields. Xcode (starting with version 14) improved refactoring support for Swift. Using automatic refactorings reduces the risk of errors compared to manual code copying.

Code Smell in Mobile Development

Mobile development adds its own specific smells related to platform constraints. In Android, this includes Context leaks, unclosed Cursors, and improper Lifecycle usage. In iOS, it is retain cycles through closures, incorrect Auto Layout handling, and giant ViewControllers. These smells not only impair maintainability but also directly affect application performance and stability.

Callback Hell is a characteristic smell for code working with asynchronous operations. Nested callbacks make code unreadable and hard to debug. Solution: coroutines (Kotlin), async/await (Swift 5.5+), RxJava/RxSwift, or Combine. According to Google I/O 2023, projects that migrated from callback style to coroutines reduced bug count by 30% and accelerated the addition of new features.

Platform Coupling — tight coupling of business logic to platform components. Testing such logic requires launching an emulator, which slows the feedback loop. Fix: Clean Architecture separates code into Domain (pure Kotlin/Swift with no platform dependencies) and Data/UI (with platform dependencies) layers. Business logic is tested on JVM without an emulator.

Frequently Asked Questions

Is Code Smell the same as a bug?

No — Code Smell is not an error. Code with a smell works correctly, but it is hard to maintain, change, and test. A bug is incorrect behavior; a smell is a warning about potential future problems.

How many smells did Martin Fowler identify?

22 smells in the second edition of “Refactoring” (2019). Among them are Long Method, Large Class, Primitive Obsession, Data Clumps, Switch Statements, Speculative Generality, and others. The community has added dozens of new smells for modern paradigms and platforms.

Which tool is best for finding Code Smell?

A combination yields the best result: Detekt (Android/Kotlin), SwiftLint (iOS), SonarQube (both) for automated analysis, and code review for semantic smells. No single tool finds 100% of problems — human experience remains decisive.

Can Code Smell be ignored?

Yes, if the code changes infrequently or will be completely rewritten soon. However, accumulating smells turns into technical debt: each new change becomes increasingly difficult, and the cost of fixing grows exponentially.

Are there smells specific to SwiftUI and Jetpack Compose?

Yes — declarative frameworks have spawned new smells: giant @State blocks, improper handling of repeated renders, excessive recomposition, and lack of Extraction into separate Views. For SwiftUI, a typical smell is Massive View with dozens of @State variables.

Summary

  • Code Smell — a surface-level sign of a deep code problem, not a bug, but reducing maintainability
  • Long Method and Large Class — the most common smells in mobile development, requiring Extract Method and Extract Class
  • Duplicate Code — duplicated logic that doubles the work with every change
  • Feature Envy and Switch Statements — signs of improper responsibility distribution between classes
  • Specific smells — God Activity, Giant ViewController, Leaking Context — unique to mobile platforms
  • Refactoring without tests is dangerous: first Characterization Tests, then small steps with commits
  • Static analysis (Detekt, SwiftLint) automates detection but does not replace code review

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also