Building for now

03 Apr 2013 in Tech

Recently, I worked on a small hack where I could ignore everything I've learned about writing maintainable software. The hack was going to be live for less than three months, so future maintenance didn't need to be considered too much. It wasn't high traffic, so performance wasn't an issue. It was development bliss - I could use lots of dirty hacks and not really care.

I tweeted as much, and received a tweet from @ryanmcdonough saying that "build for now" is the worst thing you can do in a big company, as he's learning whilst working with "shoddy non extendable code bases".

Building for now isn't synonymous with not building for the future. It's about optimising the time your developers currently have to spend on a project without impacting future maintainability.

Imagine you're working on a blog system and you want to get a post along with all it's comments. I'm not suggesting embedding queries in your controller (or worse, your view!). What I am suggesting, is writing inefficient code until it becomes a problem.

That query in a loop that can be done with joins? Use the loop, and refactor when it's an issue. If it's in a data mapper, your code doesn't know any different.

For example, imagine we have a PostsRepository for accessing posts in a database. A quick and dirty implementation might look a bit like this:

php
class PostsRepoNaive {
public function getAllWithComments() {
$posts = $this->db->fetchAll("SELECT * FROM posts");
foreach ($posts as &$post) {
$post->comments = $this->db->fetchAll("SELECT * FROM comments WHERE post_id=".$post->id);
}
return $posts;
}
}

Ignoring any security issues (that's another topic entirely), this does exactly what you want.

If one day the site becomes so busy that you just can't afford that extra query for every post, you can refactor how you get the data out

php
class PostsRepoFixed {
public function getAllWithComments() {
$posts = $this->db->fetchAll("SELECT p.*, c.id AS c_id, c.content AS c_content FROM posts p JOIN comments c ON p.id=c.post_id");
// Flatten multiple rows into $post->comments
$formatted = array();
foreach ($posts as $p){
if (!isset($formatted[$p->id])){
$formatted[$p->id] = (object) array(
"id" => $p->id,
"title" => $p->title,
"content" => $p->content
"comments" => array();
);
}
$formatted[$p->id]->comments[] = (object) array(
"id" => $p->c_id,
"content" => $p->c_content
);
}
return $formatted;
}
}

Now, you're grabbing all posts and their comments in one query, rather than n+1 queries as before. All your performance problems have gone away, and the rest of your code works just like it did before.

Using this system, if a task that would take time to implement in it's most efficient manner isn't important today, we can implement it in a less efficient way. The functionality still works, and going forward, it's easy to change the implementation if the situation calls for it.

I'm terrible for getting hung up on the details after being burned by a few projects I've worked on in the past being difficult to extend. What happens, is that I get so caught up on the details that I don't actually do any work at all. So, throw out the "right way" do to things, and do them the quickest way that you can whilst making life easy for future you. You'll be surprised just how much more you get shipped.