Discover the principle of "AHA Programming"

NicolasBrondinBernard

Author
@NicolasBrondinBernard

The DRY concept seems fraught with pitfalls to you, but no one else seems to share your view? This article should interest you.

Article published on 22/12/2021, last updated on 10/08/2026

I recently wrote an article to introduce the concept of "DRY Programming", which I invite you to read if you haven't already.

In essence, "DRY" code avoids any kind of code repetition by grouping similar pieces of code into abstractions (classes, functions, ...)

The drawbacks of DRY

In theory, it's hard to argue that "Don't Repeat Yourself" is a flawed principle, since naive code repetition is rarely something desirable in a project.

Nevertheless, excessive abstraction, or simply bad abstraction, costs more than code repetition.

This is notably what Sandi Metz argues in her article "The Wrong Abstraction".

A bad abstraction can lead to (in no particular order):

  • Accidental complexity
  • Overly heavy or incomprehensible functions/methods
  • Code maintained only to support tests, even though a large part of it is no longer actually used in the software
  • Loss aversion regarding the risk of removing or modifying said code

What about WET then?

Some people, like Conlin Durbin, have tried to give meaning to the opposite of DRY by "creating" the WET principle: Write Everything Twice.

The idea: To avoid unnecessary code abstraction, allow yourself to rewrite two copies of the same piece of code, and start creating an abstraction starting from the third version.

While the idea is charming in theory, it retains the same flaw as its antonym DRY, that is, it is dogmatic and doesn't leave enough room for reflection.

There's a whiff of "Cargo Cult" in these concepts: a principle should help us think, not replace thinking.

The compromise: AHA Programming

It was after understanding the constraints of the two previously mentioned approaches that Kent C. Dodds managed to introduce a new concept advocating the best of both worlds: AHA Programming (previously named MOIST, in reference to DRY and WET).

AHA stands for "Avoid Hasty Abstraction"

It is based on the principle previously described by Sandi Metz:

"prefer duplication over the wrong abstraction"

Based on the fact that we don't know what the future of the code we're writing will be, and that it's counterproductive to optimize the entirety of the code behind abstractions that might need to completely change due to bad assumptions, or a change of direction in business logic, Kent follows one guideline:

"Optimize for change first"

What does this actually mean?

Since we face the unknown regarding the future of our code, Kent favors duplication until we're sure and certain that these pieces of code can (and should) actually be made abstract.

The risk of starting abstraction too early lies notably in the fact that for every line that resembles, in any way, something you've already made abstract, you'll try to twist (modify) the abstraction to fit your use case.

And this continues until your function, meant to simplify the code, actually becomes unreadable due to constant refactoring and the piling up of "if" statements, to account for all the different cases.

Example

Since a line of code is worth a thousand words, here's an example of overly repetitive code, the DRY version, and the AHA version:

Note that these examples are inspired by Kent's presentation C. Dodds for the React Summit event, which I invite you to watch!

Base

// user-list.js
const name = user.name ? `${user.name.first.slice(0, 1)}. ${user.name.last}` : user.username ? `@ ${user.username}` : 'Anonymous';
console.log(name);
/*
    Possible Results : 
        - J. Doe
        - @jdoe
        - Anonymous
 */

// profile.js
const name = user.username ? `@ ${user.username}` : user.email;
console.log(name);
/*
    Possible Results : 
        - @jdoe
        - jdoe@example.com
 */

// navbar.js
const name = user.name ? `${user.name.first}. ${user.name.last}` : 'Anonymous';
console.log('Hello', name);
/*
    Possible Results : 
        - Hello John Doe
        - Hello Anonymous
 */

DRY


function getName(user, {firstnameInitial, displayEmailByDefault}){
    let name = 'Anonymous';
    if(user.name) {
        let first = user.name.first;
        if(firstnameInitial){
            first = first.slice(0, 1);
        } 
        name = `${first}. ${user.name.last}`;
    } else if(user.username) {
        name = user.username;
    } else if(displayEmailByDefault){
        name = user.email;
    }
    return name;
}

// user-list.js
console.log(getName(user,{firstnameInitial: true}));

// profile.js
console.log(getName(user, {displayEmailByDefault: true}));

// navbar.js
console.log('Hello', getName(user));

AHA


function getFullName(user, firstnameInitial){
    let name = 'Anonymous';
    if(user.name) {
        let first = firstnameInitial ? user.name.first.slice(0, 1) + '.'  : user.name.first;
        name = `${first} ${user.name.last}`;
    }
    return name;
}

function getUsernameOrEmail(user){
    let username = user.username;
    if(!username) {
        username = user.email
    }
    return username;
}

// user-list.js
console.log(getFullname(user, true));

// profile.js
console.log(getUsernameOrEmail(user));

// navbar.js
console.log('Hello', getFullname(user));

Conclusion

Neither the DRY nor the WET concepts should be thrown out, but what must be avoided at all costs is the dogmatic application of principles and overly hasty, or even unnecessary, abstraction.


Taton Moïse sur Unsplash

Finished reading this article?
Our complete courses
Take it to the next level with our courses!

Complete courses, exercises and certificates to really learn programming!

4.8 average rating

Comments (0)

to leave a comment

No comments yet