IEEE Software EssaysMy essays in IEEE Software have been on the topic of Continuous Design. As an introduction, I suggest you look at this short summary, which explains the big picture and how the essays relate.
IEEE Software - The Pragmatic Designer: Two Kinds of Iteration
This column was published in IEEE Software, The Pragmatic Designer column, January-February 2022, Vol 39, number 1.
ABSTRACT: There are two kinds of iteration, but they are commonly conflated. The first is the evolution of a design and its implementation to become more suitable over time: design-focused iteration (DFI). The second is the evolution of an artifact (like code) to become more suitable over time: code-focused iteration (CFI).
CFI improves only the code and ignores the refinement relationship between design and code. In contrast, a goal of DFI is nurturing and improving the refinement relationship so that the design becomes more stable and valuable over time.
Refactoring happens in both kinds of iteration. In CFI, the refactoring is shallow and textual. In DFI, the refactoring is conceptual and yields what Domain Driven Design calls “deep models” and “supple designs”.
IEEE Software - The Pragmatic Designer: Garbage Collect Your Technical Debt
This column was published in IEEE Software, The Pragmatic Designer column, September-October 2021, Vol 38, number 5.
ABSTRACT: The iterative process that a team follows is a bit like a garbage collection algorithm, and we can compare software development processes like we can any algorithm. A process can help developers do two things: clean up tech debt after it exists, or avoid creating it. When an iterative process does neither, tech debt buildup will lead to bankruptcy, so it is only suitable for projects with a short lifespan. A process that does both has the best chance at minimizing tech debt over a long lifespan. In particular, focusing on the system’s design will keep tech debt low.
IEEE Software - The Pragmatic Designer: Why Is It Getting Harder to Apply Software Architecture?
This column was published in IEEE Software, The Pragmatic Designer column, July-August 2021, Vol 38, number 4.
ABSTRACT: By the late 1990s, we understood the basic abstractions of software architecture, and it was possible to choose an architecture that suited the problem at hand. Over two decades later, it’s difficult to apply these ideas to our projects, but why? Because we have changed our philosophy to iterative development using factory metaphors, and have reinforced that change with tooling. Developers now focus primarily on incremental changes, and secondarily on the overall design or architecture. These circumstances are inhospitable to the holistic system reasoning that architecture requires, and can explain why so many teams today regard technical debt as their biggest challenge.
IEEE Software - The Pragmatic Designer: The Rituals of Iterations and Tests
This column was published in IEEE Software, The Pragmatic Designer column, November-December 2020, Vol 37, number 6.
ABSTRACT: Teams are right to use iterative processes and tests, but these rituals alone do not ensure a healthy project. Even with iterations and tests, teams face risks from two slippery slopes. The first is the buildup of ur-technical debt caused by a failure to refactor deeply enough, so although they intend to follow an iterative process, teams may be following a sedimentary process. The second is an over-reliance on tests to keep control of a project. By the nature of a slippery slope, a project may be in greater danger than the team perceives, so a project might already be too expensive to rescue, making it a technical zombie. To avoid these slippery slopes, teams must go beyond the mechanical rituals and invent a theory that explains the problem and solution, then ensure the code continues to act as an effective external representation for their theory.
IEEE Software - The Pragmatic Designer: Code is Your Partner in Thought
This column was published in IEEE Software, The Pragmatic Designer column, September-October 2020, Vol 37, number 5.
ABSTRACT: To understand technical debt, it’s necessary to see a program as having characteristics of both a machine and of pure thought. Technical debt arises when a program works fine as a machine but expresses my thoughts poorly. Debt in a program mentally impairs the programmers working on it. I can remove that impairment by refactoring or rewriting my program so that it again expresses my thoughts. Source code carries our thoughts with a directness and immediacy that is not possible with steel or concrete. It enables a cognitive coupling between programmers and their code, a kind of extended cognition, offloading mental burdens into the code and allowing us to produce far more complicated programs.
IEEE Software - The Pragmatic Designer: Ur-Technical Debt
This column was published in IEEE Software, The Pragmatic Designer column, July-August 2020, Vol 37, number 4.
ABSTRACT: The term technical debt was coined by Ward Cunningham in 1992. In recent years, people have broadened the definition to include ideas not present in the original formulation, including lack of skill, expedient hacking, and obliviousness to software architecture. By lumping these together, it’s harder to choose the right repair. This article proposes that we use the term ur-technical debt to refer to the original idea, which was: When I build systems iteratively, my understanding of the problem and solution grow gradually, and inevitably my current thoughts do not match code I wrote earlier, so I must expend effort to fix that code, much like paying interest on a debt.
IEEE Software - The Pragmatic Designer: Testing Numbs Us to Our Loss of Intellectual Control
This column was published in IEEE Software, The Pragmatic Designer column, May-June 2020, Vol 37, number 3.
ABSTRACT: Software teams need a healthy balance of both intellectual control, which comes from reasoning, and statistical control, which comes from testing. Complexity is the enemy of reasoning; efforts to maintain intellectual control tend to push complexity down. In my experience, many teams let their intellectual control atrophy, then compensate with more testing. This approach works for a while, but without intellectual control to keep complexity down, progress becomes slower and more difficult. Once lost, intellectual control is expensive to recover, so the teams find themselves in a local maximum they cannot escape.
IEEE Software - The Pragmatic Designer: Better Code Reviews with Design by Contract
This column was published in IEEE Software, The Pragmatic Designer column, Nov-Dec 2019, Vol 36, number 6.
ABSTRACT: Design by contract is a technique that improves the quality of your team’s code. It yields code with both a logical and a procedural nature, where the contracts state declaratively what will happen, and the implementations procedurally cause the desired effect. The team can reason either logically, by using the contracts, or procedurally, by following the code line by line, but the former allows them to reason about far larger programs. It also creates conditions for deliberate practice, so developers using design by contract will grow their design skills faster.
IEEE Software - The Pragmatic Designer: Healthy Code Reveals the Problem and Solution
This column was published in IEEE Software, The Pragmatic Designer column, Sep-Oct 2019, Vol 36, number 5.
ABSTRACT: Source code reveals abstractions from two places: the problem and the solution. It’s easier to design and evolve a system when you understand each of them separately before you combine them in code. With skill, it’s possible to separate those concerns in the code. Declarative understanding of the abstractions is the most useful and easy to convey. However, current software development processes rarely guide developers to do this.
IEEE Software - The Pragmatic Designer: Scale Your Team Horizontally
This column was published in IEEE Software, The Pragmatic Designer column, July-August 2019, Vol 36, number 4.
ABSTRACT: We’d like to add an engineer to our project and have our team get that much more power. The primary factor that allows developers to contribute is the state of the code. To keep the code clean, we must shoot for two goals to minimize a project’s ur-technical debt. First, enable developers to contribute according to their ability, not according to their tenure. Second, keep the design small enough to fit in everyone’s heads. This doesn’t contradict Brooks’ Law, which applies specifically to late projects.
IEEE Software - The Pragmatic Designer: Principle of Least Expressiveness
This column was published in IEEE Software, The Pragmatic Designer column, May-June 2019, Vol 36 number 3.
I’m always delighted to discover a connection between two ideas that I’m already fond of on their own, so I’d like to share a connection I found recently. The first idea is writing code that expresses my thinking about the problem domain, and the second is the principle of least expressiveness (PLE). The connection is that I can use the PLE to reveal my thinking about the problem domain, and because all ambiguity stops at the code, the act of programming using the PLE can help me simplify and debug the flawed ideas I have in my head.
The PLE  is as follows:
When programming a component, the right computation model for the component is the least expressive model that results in a natural program.
The least expressive model means that if you can express your idea with a constant, use that, and similarly for lookup tables, state machines, and so on. You should only use a Turing-complete language when you cannot use something simpler—with the caveat not to contort the code.
IEEE Software - The Pragmatic Designer: Ignore, Refactor, or Rewrite
This column was published in IEEE Software, The Pragmatic Designer column, 21 February 2019, Vol 36 number 2.
Imagine that you have some code written, but it has problems. The problems are small enough that you could imagine rewriting the code completely, and you must choose what do. You could do nothing (ignore it), make incremental changes (refactor it), or write new code from scratch (rewrite it). How do you choose? What factors do you consider?
There’s already a lot of guidance. In fact, the very existence of refactoring on the list of choices is special because the idea of refactoring code wasn’t well formed until the 1990s. When you refactor code, you make changes that improve its structure but do not change its visible behavior, and our tools are increasingly good at supporting refactoring, helping us make sweeping changes safely.
Most of the guidance applies to smaller chunks of code and decisions implemented in hours or days, not weeks or months. I’ve long wished that there were a great book on architecture-scale refactoring with distilled wisdom and case studies of successes and failures. This article only touches on that but it covers some topics that augment the good advice you will find in Martin Fowler’s Refactoring book  and Michael Feathers’ Working Effectively with Legacy Code . For a good case study of architecture evolution and decision making, I suggest Tony Tsakiris’s article  in which he describes how Ford looked at its vehicle control interfaces across many car brands and how it chose to embrace, tolerate, or deprecate each of them.
IEEE Software - The Pragmatic Designer: Intellectual Control
In the early days of software engineering, Edsger Dijkstra warned us not to let the size and complexity of our programs cause us to lose “intellectual control” due to the limited nature of our minds. To my knowledge, he never defined precisely what intellectual control was. Our software today is staggeringly larger than the programs of the 1960s, so does that mean we have it under our intellectual control, or did we find ways to make progress without Dijkstra’s high standards?
I see signs that we have some software that is under intellectual control and other software that is not. In this column, I’m going to discuss how we can recognize these two categories, what happens when engineers on a project have different attitudes about intellectual control, some advice on when we probably should insist on it, and some ideas about how we achieve it.
IEEE Software - Architectural Hoisting
This column was published in IEEE Software, The Pragmatic Architect column, July-Aug 2014, Vol 31, number 4.
Abstract: Architectural hoisting is a design technique where the responsibility for an intentional design constraint (that is, a guiderail) is moved away from developer vigilance into code, with the goal of achieving a global property on the system.