Simplifying Complexity: Reflections on Software Design, Code Practices, and Architectural Decisions
This article explores how software engineers can reduce both essential and accidental complexity by applying principles such as KISS, single responsibility, clear API design, and thoughtful refactoring, illustrated with real‑world Java code examples and product‑design case studies.
Background – A recent feature development revealed that a historically simple function had become overly complex, prompting a deeper look at the enduring challenge of simplifying complexity in product design and software development.
The author references Brooks' "The Mythical Man‑Month" to distinguish essential complexity (inherent to the problem) from accidental complexity (introduced by poor choices or tools).
Why Simplicity Matters – Simplicity should be global, not achieved by making one part complex to simplify another. Simple, intuitive designs reduce learning curves and maintenance costs.
Case Study: Business Type Mapping
The product design involves multiple conversion chains: Promise business type → Document type → Operation type. Although these mappings appear many‑to‑many, they are essentially one‑to‑one and can be unified to eliminate redundant concepts.
Technical code review uncovered a method with side effects:
public int filterBusinessType(Request request, Response response) {
// ... filtering logic ...
// returns an int but also mutates response
}The method returns an int while also modifying the response object, violating the Single Responsibility Principle and the Principle of Least Surprise.
Two corrected patterns are suggested:
public int filterBusinessType(String logPrefix, Request request) {
// filtering logic
int businessType = ...;
return businessType;
}
public void setResponseData(int filterResult, Response response) {
// set response fields based on filterResult
response.setFilteredData(...);
}Or return a composite object:
public FilterResultAndResponse filterBusinessType(Request request) {
int result = ...;
Response response = new Response();
response.setFilteredData(...);
return new FilterResultAndResponse(result, response);
}
class FilterResultAndResponse {
private int filterResult;
private Response response;
// getters & setters
}Additional recommendations include separating concerns, documenting side effects, avoiding static mutable state, and adhering to the Single Responsibility and Least Surprise principles.
Design Principles for Simplicity
The author emphasizes the KISS principle, user‑centered product design, and “subtract‑design” – constantly questioning whether a feature is truly needed.
Architecture advice: avoid over‑engineering, use only what is sufficient ("good enough +1"), and keep the stack simple to reduce hidden complexity.
API design should be minimal, consistent, and well‑documented, following standard naming, single‑responsibility, and concise parameters.
Automation and Standardization
Complex repetitive tasks should be tool‑enabled, documentation should be thorough, and processes should be standardized and automated (e.g., deployment orchestration).
Examples include simplifying a 30‑day calendar calculation to a 90‑day extension by copying the last day, and reducing error‑code propagation layers to expose root causes directly.
Conclusion
Simplifying complexity improves short‑term development efficiency and long‑term product and technical value; it is a continuous practice involving thoughtful design, clear code, and disciplined processes.
JD Tech Talk
Official JD Tech public account delivering best practices and technology innovation.
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.