Practical Code Refactoring Lessons from a Mobile Development Leader
The author, a mobile development team leader, shares a detailed account of a recent iOS codebase refactoring, describing the removal of unused libraries and code, architectural improvements, memory‑leak fixes, and best‑practice guidelines such as avoiding singletons, unnecessary layers, and over‑reliance on third‑party frameworks.
Update: The bugs introduced after the refactor are minimal, and as the mobile team leader I can assure you that any major mistake would have already been caught. The refactor was done two months ago; looking at the issue list, only a few bugs were added, and I’m now summarising the work.
In past personal projects I claimed three refactors on my résumé, but only the second (migration to CocoaPods) and the third (style unification) were true refactors; the first was merely a UI redesign.
At my current job, after taking over several projects, the main work has been extensive refactoring. Below is a list of the tasks performed:
Removed unused third‑party libraries.
Replaced unreasonable third‑party libraries with system alternatives or custom implementations.
Deleted variables that were defined but never used.
Removed imported header files that were never referenced.
Eliminated legacy logic from older projects that was no longer needed.
Re‑structured unreasonable hierarchies in the Controller layer and cleaned up dead code.
Re‑structured the View layer to remove unnecessary nesting.
Refactored redundant Service‑layer code.
Refactored poorly written Model‑layer code.
Decoupled tightly coupled modules.
Isolated modules that should not be coupled.
Fixed multiple memory leaks.
Fixed several retain‑cycle (circular reference) issues.
Optimised compilation speed.
Eliminated all project warnings.
In one project we deleted over 90,000 lines of third‑party code from the Pods folder and about 40,000 lines of dead code from the project itself; the app still ran perfectly after the deletions.
Further refactoring involved removing another 12,000 lines of code while adding roughly 1,100 lines of new, cleaner code.
Overall, compilation time dropped from 2‑3 minutes to about 40 seconds, warnings fell from over 70 to zero, third‑party libraries were reduced from 51 to 13, the app bundle shrank from 22.1 MB to 3.7 MB, and functionality actually increased.
Memory‑leak issues were solved by releasing C‑allocated memory promptly and improving call patterns, eliminating hundreds of kilobytes per use.
Circular references were uncovered after re‑enabling Xcode warnings, revealing four retain cycles and dozens of warnings that caused crashes when a page was repeatedly opened and closed.
Avoid Overusing Singletons
Singletons feel convenient, but they persist for the entire app lifecycle; using them for modules that are rarely needed only wastes memory.
Avoid Unnecessary Layering
For example, a network stack might have separate layers for AFNetworking wrapper, API suffix, request construction, OAuth, and plain requests—six files to modify for a single new API. Consolidating these into a single, well‑designed layer reduces complexity.
Design Reasonable Method Names
Method signatures should not expose ambiguous parameters. Compare:
- (void)requestAtPathForRouteNamed:(NSString *)routeName object:(id)object parameters:(NSDictionary *)parameterswith
- (void)requestWithMethod:(XXHTTPMethod)method path:(NSString *)path params:(id)params paramsType:(XXParamType)paramsTypeThe second design uses a single params argument whose type is clarified by an enum, preventing misuse.
Avoid Unnecessary Coupling
A gradient image utility was previously tied to UIColor+XXTheme and mis‑named; after refactor it became a proper category method:
+ (instancetype)xx_gradientImageWithStartColor:(UIColor *)aColor endColor:(UIColor *)bColor andWidth:(CGFloat)widthAvoid Overusing Inheritance
Inheritance can cause every subclass to execute costly base‑class code. In our app, all view controllers inherited from a base class that performed a heavy operation, leading to frequent freezes. Replacing inheritance with a category that adds needed methods on demand solved the issue.
Choose Third‑Party Libraries Wisely
When a library is necessary, prefer one with many GitHub stars, an active issue tracker, and good compatibility.
Avoid Unnecessary Third‑Party Libraries
We replaced YYText (which caused crashes on iOS 9) with a simple NSAttributedString attributedStringWithAttachment implementation of seven lines, removing an unstable dependency.
Older libraries like PSCollectionView were also removed because the system provides sufficient functionality.
Let Each Layer Do Its Job
Data‑related tasks such as UTF‑8 encoding or timestamp formatting belong in the Model layer.
Adapt to Real Requirements
Back‑end services store IDs as long for indexing, but the client can safely use NSString. Similarly, monetary values are best kept as strings on the client and converted only when calculation is needed to avoid precision loss.
Naming Conventions
Refer to Apple’s UIKit for naming inspiration when creating new components.
Avoid Meaningless Comments
Objective‑C method names are already descriptive; adding spaced‑out comments adds no value.
Delete Unused Code
Git provides version history, so commented‑out code should be removed to keep the codebase clean and readable.
Refactoring legacy code can feel like a “once‑in‑a‑lifetime” experience, but solving these small issues improves maintainability for future developers.
Source: https://www.pupboss.com/the-impression-of-code-refactoring/
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
