The Difference Between Code as an Asset and Code as a Liability
When an expert throws clay pots, the quality of the clay is a critical factor in determining the outcome. The potter wants plasticity, which means the clay is mouldable and malleable. In the modern era, most companies are digital or transforming to be so. Their medium is the source code of their software systems. Just as the potter wants adaptable clay, the organisation wants adaptable and evolvable software systems. The organisation wants plasticity in this source code so it can mould and adapt its systems to enable business agility. This article covers both what not to do and what to do to achieve high-quality, adaptable code.
Is your team building a strategic asset or a technical debt liability? The difference almost always comes down to one critical failure: focusing on the trees (immediate functionality) and missing the forest (the overall architectural vision). When someone “just hacks it in,” there is no vision, no high-level goals, no forest view. They dive straight into the details—straight to the trees. Without that broader perspective, the team suffers the following consequences:
Instead, there’s a laser focus on the narrow area of interest. Implementation decisions are made only to solve the immediate problem in that tiny corner. The results are predictable:
With this shared understanding becomes impossible—only the original author (maybe) understands it.
No Vision = No Boundaries
Without high-level goals, the solution has no clear architectural borders. This lack of definition leads to components that are tightly coupled and conceptually confusing:
Trees Without Forest = Tactical Myopia
Narrow focus produces local decisions that are globally incompatible. When developers only focus on the immediate ticket, they create code that works today but poisons the future:
No Principles = Architectural Drift
When fundamental design principles aren’t enforced, system entropy wins. This causes the codebase to drift away from any sensible design, making it fragile:
Code-Level Symptoms (Code Smells)
These additional signs are often the direct results of the problems listed above and signal a significant architecture problem:
This code is not an asset; it is a liability that drains budgets and slows feature velocity. Hopefully, code only gets this fragile at its end-of-life—10 to 15 years in. If a new solution is hacked together, you start at the end-of-life state on day one.
The cure for hacking is deliberate architectural vision and governance. Why is this important? Technical debt is a compounding relationship, much like a compound interest account. New interest is incurred on existing interest. similarly new technical debt is incurred on existing technical debt. No company can sustain the exponential costs associated with this.
This is why software architecture and design are essential. It is not an abstract activity performed by purists; it is the fundamental way we ensure software systems can be changed cheaply and reliably, now and in the future. It is a well-accepted fact that:
The initial project cost that "births" a software system is usually insignificant compared to the cost of changing that software over its lifetime.
We design to minimise those future, long-term costs.
The solution
So, how do we avoid this trap? The cure for hacking isn't more process, but deliberate architectural vision and governance.
Scrum and Agile software development aren't just about meeting names and formats—they also prescribe engineering practices that shape maintainable code. Maintainable code is code that can be changed cheaply and reliably, now and in the future. These changes include bug fixes, enhancing existing features, or adding new features. This article proceeds by describing the Agile design track. Aligning with this track will provide far more agility than aligning only with the Scrum track.
The Design Track is not a one-time activity; it's followed iteratively within and across every sprint. While some high-level architectural work is done during sprint planning, the bulk of the detailed design, application of design principles, and refinement happens continuously throughout the rest of the sprint. This constant, iterative focus is what keeps the code plastic.
Software needs engineering principles just like bridges do. Everyone understands that building a bridge requires proven engineering principles. No one starts pouring concrete and hopes for the best. We all accept that if those principles are ignored, the structure becomes unstable — and eventually collapses. Because it’s physical, we can spot the design flaws and can often see the cracks forming. In software, these cracks are often invisible until it's too late
Software is no different. It too must follow rigorous principles to remain stable. The difference? Software is invisible, an abstract logical structure. You can’t spot a missing beam or a untightened bolt just by looking. So what does “unstable” even mean in software? Stability is simply the ability to change the software quickly, safely, and without fear.
Software that is hard to change is unstable. When change is no longer possible, the codebase has effectively collapsed, even if it still runs. This is the moment the "clay" loses its plasticity. Here are the most important design principles that keep software strong, scalable, and maintainable.
The 5 SOLID Principles – Your Code’s Best Friends
Three More Golden Rules
Automated testing, the enabling practice
Automated testing is the #1 enabler of agile development. It's the crucial safety net that lets teams refactor (restructure code without changing behaviour), extend, and improve the system with confidence—knowing immediately if a change accidentally broke existing functionality.
Ubiquitous Language is the practice of getting everyone—clients, domain experts, designers, and developers—to use the exact same vocabulary when discussing the real-world business domain. It starts by accurately understanding the domain and iteratively adding detail until a clear model emerges that drives the implementation of apps, services, and databases.
This step is foundational as without conceptual clarity, agility is impossible. The concepts and ideas in both the domain and the solution will not be clearly understood or expressed, leading directly back to the architectural chaos discussed earlier.
Fortunately, this is also the step where the business analyst's, UI designer's, and solution architect's practices overlap. Domain modelling is the one thing they all do.
Business analyst
This is where the business analysts will perform Concept and Data modelling. Concept modelling organises the business vocabulary, focusing on the core noun concepts of the domain. High value descriptions of the concepts, which are free from data or implementation bias are created.
Data modelling describes the entities, classes or data objects relevant to a domain. It describes their attributes, and the relationships among them. The model provides a common set of semantics for analysis and implementation.
UI designer
Designers focus on Information Architecture, the discipline of organising information for the user interface. To create an effective UI, they must first deeply analyse the underlying business domain: the entities, their attributes, relationships, and the overall quantum of data.
This analysis, which benefits from practices like Concept, Data, and Domain Modelling, ensures that the resulting user interface is appropriately designed. Crucially, the designer's work ensures the final user interface visually and functionally maps directly to the domain's agreed-upon ubiquitous language.
Solution architect
Object-Oriented Analysis and Design (OOA/D) is what sets object-oriented programming apart from other approaches. Before OOA/D, analysis and design were two unique activities. OOA/D changed this, requiring one to start by analysing the problem and continuously add more detail until the design for the system's components emerge. Domain-Driven Design (DDD) is similar, just a bit more opinionated when it comes to classifying the classes and defining their purpose.
Everyone
This step is foundational, so it's not surprising that it's practiced by business analysts, designers, and architects. Without it, agility of the solution is simply not possible. Without it, the concepts and ideas in both the domain and the solution will not be clearly understood and expressed separately.
Agile says it best: Ubiquitous language strives to use the vocabulary of a given business domain, not only in discussions about the requirements for a software product but in discussions of design as well and all the way into “the product’s source code itself”.
The goal is simple: to refine ideas and express concepts clearly, consistently, and separately so that the language of the business becomes the language of the code. This direct connection is the ultimate source of architectural plasticity.
Recommended by LinkedIn
The ubiquitous language/domain model produced drives the implementation of the Model layer in all components making up the system. It shapes database schemas and core business logic. As solution details are added (like the user interface, or View), the Model must be connected to it. This connection is built iteratively and incrementally by adding details to the Model, View and the connecting layer, which is given different names like Controller, Presenter, ViewModel, etc.
The critical principle here is Model independence. The Model (business logic and persistence) must be entirely independent of the View and Controller that use it. It should be possible to put a CLI, GUI, batch file, or any other interface on top of the Model. It should also be possible to embed the Model in multiple system components, improving performance by turning remote calls into local, direct invocations.
The main focus should be to keep the process and design as simple as possible. The following principles should be adhered to:
Agile promotes Emergent Design, the idea that good global design results from consistently paying attention to the local qualities of code structure.
While this sounds like hacking—as both involve focusing locally on the code being changed—the critical difference is discipline and adherence to the vision:
By enforcing this discipline locally, the global architecture emerges as stable, maintainable, and robust—the exact strategic asset that hacking turns into a liability.
A common misconception about emergent design is that it reduces the need for design skill. In reality, the opposite is true. Significant design skill is required to ensure the correct design emerges. Furthermore, design is a continuous activity performed by all developers.
The same pattern repeats daily: Refactor the code first so that once the increment is complete, the code still adheres to SOLID, DRY, etc. This refactor first approach is the daily practice that separates emergent design from hacking. It is the application of significant design skill that results in the minimisation of later refactoring. Design is a team activity, and everyone should be contributing. If this is not the case, then the team does not possess the significant design skill required.
In corporations where software is not considered the main product, you often hear the instruction: "Implement the change, but do not touch the existing code." While software may not be the primary product, it is the main business enabler—a fact confirmed by the importance of digital transformation. This instruction to not change the existing code is a guarantee that the system will violate design principles and rapidly turn that vital business enabler into a business inhibitor. Refactor First is the daily practice that prevents this decay.
Automated testing is the crucial safeguard. It is why the Testing track is equally important in Agile software systems. When performed with a safety net, refactoring provides clear benefits:
What Refactoring Looks Like
Refactoring is when the code structure is changed without changing its external behaviour. One is refactoring when one:
The Refactor-First Discipline
The code must adhere to all design principles after a refactor. Code is usually refactored to “prepare the ground” before new functionality is laid down, ensuring that after the new code is added, the design principles are still present.
This refactor-first approach is the daily discipline that separates emergent design from hacking. It is a continuous activity, which, when coupled with significant design skill, actually minimises the amount of refactoring needed over the system’s lifetime.
Simple Design establishes the goal, the Rules of Simplicity provide the objective measure to determine if that goal has been met. These criteria act as the ultimate definition of high-quality, maintainable code, ranked by priority:
1. Code is Verified by Automated Tests
The code must fulfil all requirements and perform its intended function. Most importantly, all automated tests must pass, confirming reliability and functionality.
2. Code Contains No Duplication (DRY)
This rule eliminates redundancy in logic and knowledge. Each piece of knowledge exists in exactly one place, including the duplication of concepts, not just code.
3. Code Expresses Each Distinct Idea Separately
The code must communicate its purpose clearly. This means:
4. Fewest Elements
The design uses the minimal number of classes, methods, and lines of code possible. This involves having no unnecessary abstractions or complexity, and removing anything that doesn't serve the first three, higher-priority rules.
Strategic purposes of Rules of simplicity
The Rules of Simplicity serve high-level, strategic objectives for the business:
The Tactical purposes of Rules of simplicity
On a day-to-day level, these rules provide clear guidelines for the development team:
While developers handle local design decisions as part of their routine work—emphasising that design is pervasive, ongoing, and done by all—some decisions have far-reaching architectural consequences. It is crucial that all developers possess the design skill to recognise when a decision moves from being a tactical choice to a strategic one.
Agile is not about no design; it is about continuous design rather than big up-front design.
For strategic decisions, the team must employ just-in-time design via quick design sessions. Two or, preferably, more developers should meet to discuss and align on the proposed path. These sessions address strategic design issues, contrasting with refactoring, which deals with local design improvements.
To make these discussions effective, apply these two guidelines:
Successful, emergent design requires high-fidelity team interaction using low-fidelity, transient artefacts (like whiteboards or simple sketches). This reinforces Agile Principle 6: The most efficient and effective method of conveying information to and within a development team is face-to-face conversation.
CRC cards (Class-Responsibility-Collaborator) is a technique often employed during quick design sessions. Two or more team members list the most important objects, concepts, events, or transactions involved in the feature onto simple index cards. To each card are added its responsibilities (what it does) and its collaborators (who is does it with).
The design is then validated using the plausible scenarios as described in quick design sessions.
While the information on these cards is similar to what you'd find on a class diagram, the key difference is their physical, index-card nature. This tactile nature forces a more human, conversational, and collaborative interaction, which is highly efficient for refining a shared understanding of the solution. Agile Principle 6 (face-to-face conversation) comes to mind again.
Conclusion
We began by defining the difference between code that enables and code that inhibits. The reality is that correctly engineering software systems is not merely a technical exercise; it is a powerful business tool. By adhering to the Agile Design Track, practicing Refactor-First, and diligently applying principles like SOLID and DRY, we transform our source code into the malleable, adaptable clay the modern digital business requires.
This code plasticity is the key enabler for a business to have software systems that sustain and accelerate its agility. It ensures that the enterprise can quickly, safely, and cheaply respond to market changes, delivering new features and fixing bugs without the compounding decay of technical debt.
Ultimately, the commitment to architectural discipline yields a far greater reward than mere stability: it restores the intrinsic satisfaction of the craft. Crafting such systems is a pleasure and joy, because every local change contributes to a robust, coherent global vision. And when the code is clean, expressive, and predictable, the ongoing maintenance and evolution become painless. Running such systems is a pleasure and joy, freeing teams from the relentless grind of firefighting and allowing them to focus on innovation and value delivery.
The choice is simple: will you allow tactical myopia to forge a costly liability, or will you commit to the architectural vision that creates a strategic, adaptable asset? The future of your business rests on the plasticity of your code.