Code Refactoring: Concepts, Practices, and Techniques
The article shares practical lessons from refactoring a ride‑hailing order system, explaining Martin Fowler’s definition of refactoring, distinguishing small (renaming, deduplication) and large (architectural redesign) efforts, outlining typical code smells, and detailing a structured pre‑, during‑, and post‑refactoring process to improve maintainability, reduce bugs, and accelerate development.
Recently the author participated in the refactoring of the Gaode ride‑hailing order Push project and shares the experience of code refactoring to inspire developers.
The only effective metric for code quality is: WTFs (what the fuck) per minute.
When working on a feature, developers often spend a lot of time locating related code, or feel "scared" when reading someone else’s messy code with unclear structure, vague variable and method names. This is not the developer’s fault; the code itself needs refactoring.
What is Refactoring?
As a noun (Martin Fowler): Refactoring is an adjustment of the internal structure of software, aimed at improving understandability and reducing modification cost without changing observable behavior. As a verb (Martin Fowler): Refactoring uses a series of refactoring techniques to adjust the structure without changing observable behavior.
The article focuses on the verb definition of refactoring and distinguishes between small refactoring and large refactoring .
Small Refactoring
Small refactoring deals with details at the class, function, or variable level, such as renaming poorly named variables, eliminating huge functions, and removing duplicate code. It is usually localized, simple, and can be done during daily development.
Large Refactoring
Large refactoring targets the top‑level architecture, including system, module, code, and class relationships. It often involves service layering, module modularization, componentization, and abstraction reuse. Large refactoring requires redefining principles, patterns, or even business logic and carries higher risk and longer duration.
Why Refactor?
Maintain a good software architecture that can provide stable services and handle incidents gracefully.
Increase maintainability and reduce maintenance cost, creating a positive feedback loop for teams and individuals.
Accelerate development speed and reduce labor cost; a well‑designed codebase prevents the system from “rotting” and speeds up feature delivery.
Common concerns that discourage developers from refactoring include lack of experience, unclear short‑term benefits, fear of introducing bugs, and extra effort for code they did not write.
Typical Code Smells
Generic Erasure
//{"param1":"v1", "param2":"v2", "param3":30, ……}</code><code>Map map = JSON.parseObject(msg); //【1】</code><code>……</code><code>// Pass map to lower‑level interface</code><code>xxxService.handle(map); //【2】</code><code><br/></code><code>// Lower‑level interface definition</code><code>void handle(Map<String, String> map); //【3】Passing a raw Map to a generically typed interface can cause ClassCastException when non‑String values appear.
Useless Comments
Config config = new Config();</code><code>// set name and md5</code><code>config.setName(item.getName());</code><code>config.setMd5(item.getMd5());</code><code>// set value</code><code>config.setTypeMap(map);</code><code>// log</code><code>LOGGER.info("update done ({},{}), start replace", getName(), getMd5());Comments that do not add information become noise.
Excessive if‑else
try {</code><code> if (StringUtils.isEmpty(id)) {</code><code> if (StringUtils.isNotEmpty(cacheValue)) {</code><code> if (StringUtils.isNotEmpty(idPair)) {</code><code> if (cacheValue.equals(idPair)) {</code><code> // xxx</code><code> } else {</code><code> // xxx</code><code> }</code><code> }</code><code> } else {</code><code> if (StringUtils.isNotEmpty(idPair)) {</code><code> // xxx</code><code> }</code><code> }</code><code> if (xxxx(xxxx){</code><code> // xxx</code><code> } else {</code><code> if (StringUtils.isNotEmpty(idPair)){</code><code> // xxx</code><code> }</code><code> // xxx</code><code> }</code><code> } else if (!check(id, param)) {</code><code> // xxx</code><code> }</code><code>} catch (Exception e) {</code><code> log.error("error:", e);</code><code>}Deeply nested conditionals severely hurt readability and increase the risk of bugs.
Other Smells
Duplicate code – identical code blocks in multiple places should be extracted into a common method or class.
Long functions – violate the Single Responsibility Principle.
Poor naming – names should be self‑explanatory.
Useless code – dead code or commented‑out blocks should be removed.
God classes – classes that do too many things should be split according to responsibilities.
Large Refactoring Process
Pre‑Refactoring
Clarify the content, purpose, direction, and goals of the refactoring.
Gather data: map existing business, architecture, service dependencies, and produce design diagrams.
Project initiation: hold meetings, announce timeline, assign owners, and communicate impact.
During Refactoring
Architecture design and review – iterate until the team reaches consensus.
Detailed implementation plan – cover coding, unit testing, integration testing, AB testing, and rollout strategy.
AB validation – run the same input through old and new flows, compare results, and control rollout via gray‑scale and execution switches.
Code development, testing, and offline deployment – ensure test coverage and AB validation before production rollout.
Post‑Refactoring
Online rollout – gradual traffic increase (1%, 5%, 10%, …) while monitoring errors and metrics.
Business execution switch – after validation, enable write operations in the new flow.
Final cleanup – decommission old logic and AB validation code, conduct a retrospective to capture lessons learned.
Images illustrating the process are omitted for brevity.
Conclusion
Key coding tips include adhering to single‑responsibility, depending on abstractions rather than concrete implementations, following coding standards, using TODO/FIXME/XXX tags, and maintaining comprehensive unit, integration, and functional tests.
Remember: we are the authors of code, and future developers are our readers. Write code that future developers can understand and maintain, and avoid the "broken‑window" effect by continuously improving the codebase.
Amap Tech
Official Amap technology account showcasing all of Amap's technical innovations.
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.
