Practicing YAGNI - Show me the code
Tags: practices, programming, productivity
Published: 2018-01-29 18:08:43

---
title: Practicing YAGNI - Show me the code
published: true
description: A write-up of what practicing YAGNI looks like in code.
tags: practices, programming, productivity
cover_image: https://thepracticaldev.s3.amazonaws.com/i/bwxgt4f2btmdyfzyzatd.png
---
After my [Practicing YAGNI](https://dev.to/gonedark/practicing-yagni-3n1d) post, many asked to see what practicing YAGNI looks like _in code_.

As a quick recap - YAGNI is a principle of [eXtreme Programming](http://www.extremeprogramming.org/). YAGNI is an acronym for You Aren't Gonna Need It.

I think Ron Jeffries, one of the co-founders of eXtreme Programming, summarizes practicing YAGNI well:

> Implement things when you actually need them, never when you just foresee that you need them.

## Why Practicing YAGNI is hard

Despite the fun acronym and straightforward summary, programmers often succumb to over-engineering, implementing new shiny tech, or adding additional features. Even if we overcome these urges, it's still unclear what practicing YAGNI means in code. After all, we're programmers and code speaks louder than words.

Rasmus Lerdof said to me once, _"Show me the code"_. So I'm going to spend the rest of this article working through a series of [stories](https://www.mountaingoatsoftware.com/agile/user-stories). By practicing YAGNI, we'll see how the code evolves naturally, not inherently. These code samples are written in PHP to honor Mr. Lerdof.

## The First Story

> _As_ marketing I want to get a list of all users so I can perform analytics.
> _Given_ I access a specific URL
> _Then_ I should get a list of all users in JSON format

The first story is the most challenging. This is when our urges are the greatest. Remember, YAGNI tells us to only implement what we actually need. Or, said another way, write the simplest thing possible.

Here is what that code might look like.

```php
<?php
$mysqli = new mysqli('localhost', 'dbuser', 'dbpass', 'dbname');

$query = 'SELECT * FROM users';
$result = $mysqli->query($query);
$users = $result->fetch_all(MYSQLI_ASSOC);

header('Content-Type: application/json');
echo json_encode(['users' => $users]);
```

Admittedly this code is not elegant. It'll probably get you down voted on Reddit or StackOverflow. [The Pragmatic Programmer](https://pragprog.com/book/tpp/the-pragmatic-programmer) discusses writing _"Good Enough Software"_, and that's exactly what this is. At just 7 lines of code it's undeniably simple. Any junior programmer can readily work with the code. Most importantly, it completes the story. Nothing more. Nothing less (well, technically, we could have dumped the `users` table in JSON format and served a static file).

## The Second Story

> _As_ legal I don't want the user's password to be in the response so we don't get sued.
> _Given_ I access the URL for the user list
> _Then_ I should get a list of all users in JSON format
> _And_ it should not include their password

So what's the simplest thing we can possibly do? Well we can adjust the SQL query to only select the _appropriate_ fields.

```php
<?php
$mysqli = new mysqli('localhost', 'dbuser', 'dbpass', 'dbname');

$query = 'SELECT email, role FROM users';
$result = $mysqli->query($query);
$users = $result->fetch_all(MYSQLI_ASSOC);

header('Content-Type: application/json');
echo json_encode(['users' => $users]);
```

## The Third Story

> _As_ marketing I want to filter the user list by email address so I can group user data more easily.
> _Given_ I access the URL for the user list
> _And_ I set an "email" parameter
> _Then_ I should get a list of all users in JSON format whose email address contains the "email" parameter value

Again, let's adjust the SQL query based on the `email` parameter.

```php
<?php
$mysqli = new mysqli('localhost', 'dbuser', 'dbpass', 'dbname');

$query = 'SELECT email, role FROM users';
if (!empty($_GET['email'])) {
    $query .= 'WHERE email LIKE \'%' . $_GET['email'] . '%\'';
}

$result = $mysqli->query($query);
$users = $result->fetch_all(MYSQLI_ASSOC);

header('Content-Type: application/json');
echo json_encode(['users' => $users]);
```

Now, while this completes the story, there are a few things we should not continue to ignore.

- **Increased complexity**. The `if` statement introduces a new logical branch of the code.
- **Rate of change**. Each of the recent stories has required changing the SQL statement.
- **Security risk**. The added code is vulnerable to SQL injections.

These reasons should guide us to refactor our code. A common misconception is YAGNI and refactoring oppose one another. I find their relationship to be more symbiotic. YAGNI keeps my code simple, which makes it easy to refactor. If my code is easy to refactor, then I may quickly rework the code at any time. This gives me confidence to continue writing simple code and perpetuate the cycle.

So, let's refactor to extract the complexity. Being more familiar with the code at this point, a _Repository_ object is an obvious candidate. In addition, we'll also ensure the Repository mitigates our risk of SQL injection.

```php
<?php
require 'vendor/autoload.php';

$mysqli = new mysqli('localhost', 'dbuser', 'dbpass', 'dbname');

$userRepository = new UserRepository($mysqli);
$users = $userRepository->getUsers($_GET['email']);

header('Content-Type: application/json');
echo json_encode(['users' => $users]);
```

## The Fourth Story

> _As_ marketing I want to see a description for "role" so I can better understand the data.
> _Given_ I access the URL for the user list
> _And_ the user's role is 1 or 2
> _Then_ I should see "Member" or "VIP" (respectively) for "role"

There are multiple implementations available, two quickly come to mind. We can implement this in the SQL statement or we could implement this with code. While updating the SQL statement is arguably the simplest, it dramatically increases its complexity. For this reason, I chose to implement this with code.

```php
<?php
require 'vendor/autoload.php';

$mysqli = new mysqli('localhost', 'dbuser', 'dbpass', 'dbname');

$userRepository = new UserRepository($mysqli);
$raw_users = $userRepository->getUsers($_GET['email']);

$users = [];
foreach ($raw_users as $user) {
    $users = [
        'email' => $user['email'],
        'role' => $user['role'] == 2 ? 'VIP' : 'Member'
    ];
}

header('Content-Type: application/json');
echo json_encode(['users' => $users]);
```

This code works, however just as before it introduces new code branches and business logic. It also doubles our line count. So, I'll use some of the time I saved writing this simple solution to see if there's a more straightforward option. Indeed there is, PHP has a `JsonSerializable` interface we can implement to return a value to `json_encode()`.

I can introduce a simple `User` object which implements `JsonSerializable` and move the new logic there. I'll update `UserRepository` to return an array of `User` objects. The final code is the same as when I started the story, proving a successful refactor.

```php
<?php
require 'vendor/autoload.php';

$mysqli = new mysqli('localhost', 'dbuser', 'dbpass', 'dbname');

$userRepository = new UserRepository($mysqli);
$users = $userRepository->getUsers($_GET['email']);

header('Content-Type: application/json');
echo json_encode(['users' => $users]);
```

## The Fifth Story

> _As_ Finance I need to get the user list in XML format so we can import it into our archaic systems.
> _Given_ I access the URL for the user list
> _And_ I set my "Accept" header to "application/xml"
> _Then_ I should get a list of all users in XML format

You're getting the hang of this now, so let's just add some `if` statements to vary the response.

```php
<?php
require 'vendor/autoload.php';

$mysqli = new mysqli('localhost', 'dbuser', 'dbpass', 'dbname');

$userRepository = new UserRepository($mysqli);
$users = $userRepository->getUsers($_GET['email']);

if (strpos($_SERVER['HTTP_ACCEPT'], 'application/xml') !== false) {
    header('Content-Type: application/xml');
    // output the XML...
} else {
    header('Content-Type: application/json');
    echo json_encode(['users' => $users]);
}
```

We could continue down this path, but we would reintroduce code branches. If you were test driving this code with [TDD](https://en.wikipedia.org/wiki/Test-driven_development) (another key XP principle) you might have noticed this while adding contexts to test the different code paths. So let's use this opportunity to introduce an object.

```php
<?php
require 'vendor/autoload.php';

$mysqli = new mysqli('localhost', 'dbuser', 'dbpass', 'dbname');

$userRepository = new UserRepository($mysqli);
$users = $userRepository->getUsers($_GET['email']);

ContentNegotiator::sendResponse($user);
```

In this case a simple content negotiator we pass the user data to. It might even be static as it has no state and simply outputs a response.

## Retro

Let's take a look at what practicing YAGNI afforded us:

- **Speed**. We completed 5 stories quickly and easily as YAGNI kept us focused.
- **Simplicity**. We actually decreased the size of the initial code. We introduced new objects as they were needed, improving their cohesion.
- **Stress-free**. Finally, we achieved all this without stress. We didn't get "stuck" worrying about the architecture or scope.

Hopefully you find the same benefits from practicing YAGNI as I have.

---

_**Want more?** Follow [@gonedark](https://twitter.com/gonedark) on Twitter to get weekly coding tips, resourceful retweets, and other randomness._