Why Software Complexity Is Killing Developers and How to Tame It
The article explores the nature of software complexity, contrasting rational metrics like McCabe cyclomatic complexity with the subjective view of John Ousterhout, identifies symptoms such as change amplification, cognitive load and unknown unknowns, and proposes strategic design over tactical programming to control architectural decay.
Introduction
A doctor and a civil engineer argue about the oldest profession, while a software engineer asks who created the chaos, setting the stage for a discussion on software complexity.
What Is Complexity?
Complexity can be measured rationally (McCabe’s cyclomatic complexity) or perceived subjectively (John Ousterhout’s view). McCabe introduced cyclomatic complexity in 1976 to quantify code structure, testability, and maintenance cost.
Rational Metrics
McCabe’s metric classifies code by cyclomatic ranges, linking low complexity to clear structure, high testability, and low maintenance cost, while higher ranges indicate increasing difficulty.
Subjective Perception
Ousterhout defines complexity as anything that makes software hard to understand or modify.
Forms of Complexity
Three main symptoms are identified:
Change Amplification : Simple changes require modifications in many places.
Cognitive Load : The amount of knowledge a developer must have to complete a task.
Unknown Unknowns : Unclear which parts of the code need to change.
Examples include a CRM method that checks capacity and would need changes across many scenarios when business rules evolve.
Why Complexity Arises
Factors such as shortcut solutions, lack of craftsmanship, insufficient technical skill, and poor hand‑over practices contribute to growing complexity.
Inherent Software Complexity
Software complexity is an intrinsic property, not accidental, encompassing domain difficulty, development process challenges, flexibility needs, and discrete system behavior.
Architectural Governance
Software architecture evolves from monoliths to micro‑services, aiming to manage complexity. Three programming paradigms—structured, object‑oriented, and functional—are discussed, along with SOLID principles.
Increasing Complexity
Complexity tends to rise unless actively managed. The article lists three viewpoints: ambiguity creates complexity, dependencies spread it, and incremental changes often hide costs.
Tactical Programming
Fast, short‑term solutions (tactical programming) prioritize speed over long‑term quality, leading to “tactical tornadoes” that accelerate decay.
Strategic Programming
Strategic programming focuses on sustainable design, limiting unnecessary complexity, making incremental improvements, and investing 10‑20% of effort in design.
System Dilemma and Evolution
When a system becomes too complex, strategic redesign becomes difficult, and organizations fall back to tactical fixes.
Architectural Myths
Two common myths are examined: that good code is self‑documenting (a myth) and that elegant code is always the goal, ignoring practical constraints.
Final Thoughts
Software engineering is a lifelong challenge of balancing complexity, elegance, and practicality. References to key books are provided.
References
A Philosophy of Software Design
Object‑Oriented Analysis and Design with Applications
Clean Code
Clean Architecture
Patterns of Enterprise Application Architecture
/**
* 销售捡入客户
*/
public void pick(String salesId, String customerId) {
// 查询客户总数
long customerCnt = customerDao.findCustomerCount(salesId);
// 查询销售库容
long capacity = capacityDao.findSalesCapacity(salesId);
// 判断是否超额
if(customerCnt >= capacity) {
throw new BizException("capacity over limit");
}
// 代码省略 do customer pick
} @HSFProvider(serviceInterface = AgnDistributeRuleConfigQueryService.class)
public class AgnDistributeRuleConfigQueryServiceImpl implements AgnDistributeRuleConfigQueryService {
@Override
public ResultModel<AgnDistributeRuleConfigDto> queryAgnDistributeRuleConfigById(String id) {
logger.info("queryAgnDistributeRuleConfigById id=" + id);
ResultModel<AgnDistributeRuleConfigDto> result = new ResultModel<>();
if(StringUtils.isBlank(id)){
result.setSuccess(false);
result.setErrorMsg("id cannot be blank");
return result;
}
try {
// business logic ...
} catch (Exception e) {
logger.error("queryAgnDistributeRuleConfigById error,", e);
result.setSuccess(false);
result.setErrorMsg(e.getMessage());
}
return result;
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
21CTO
21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.
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.
