Refactoring to Understand

I’ve been working on a large front-end application for the past several months. The code base is mature and has its own unique style. I started out on the project adding new features, following the patterns already established. I didn’t consider the architecture much — partly because I didn’t need to, and partly because it was overwhelming. After I became familiar with it I began to understand some of the patterns, but still could not grasp the entirety of it.

It wasn’t until I had the chance to refactor some pieces that it finally clicked. Looking at the code with the ‘refactoring’ mindset opened up my view. When writing a new feature, writing new code, it’s easy to get tunnel vision — follow existing patterns because they’re understood. The release deadline is close, this piece is blocking the next task, whatever — they’re all reasons to focus small. Refactoring made me think about abstractions over specifics, which forced me to reason about the system.

A refactoring mindset pushed me focus on why a function or module was written as it was. The reasons for focusing small don’t exist. Were there assumptions that proved to be false? Does this function serve its original intent? Same question, but for the entire module? Does following existing patterns negatively affect how this function was written? It also exposes bad testing — if I refactor the implementation of a function and tests fail, were those tests just noise? Again, it pushes focus to the why, rather than tunnel vision on how.

I haven’t spent much time thinking about it besides this — I had a spark of insight and decided to write it down. I could see the argument that code reviews should catch those why questions, or that architectural concerns should always be in mind when writing new code. They aren’t always though, and looking at code with refactoring in mind helps me read it differently — and better.

Task management, planning, and monthly logs

 

I work in a role where I am largely self-directed. I do not have a technical lead or manager, I report to our creative director. That puts me in a unique role, especially as my work requires a breadth of development responsibilities. I set up servers, do back and front end development, and meet with clients to discuss everything my role touches. Without formal technical direction, planning work is as much my responsibility as actually doing it.

Since I want to be good at my job, I had to figure out the best system to manage and plan tasks, keep a record of my progress, and organize any notes I take. I needed to make something efficient but also platform independent. I’ve burned myself getting too attached to a service (cough, Evernote, cough), that made switching take a lot of time and effort.

I’ve followed the GTD system for a while, and that informed some of the decisions I made about how to structure my own system. I added what extra I needed and de-emphasized the pieces that are less applicable to me. I use the theory, if not the direct methods.

Since this system works for me, and has for a sustainable amount of time, I thought it was worth sharing. There are two components, the structure and the workflow.

Structure

The entire system relies on text — which lets me move between editors, cloud syncing providers, platforms; it can go anywhere I need it. I make one exception for Trello, since it’s so versatile and I can export my entire account to JSON should I need to. The following are the tent-poles of the system:

Trello

Trello is where tasks are kept, categorized, prioritized, shared, and completed. Every task has a board where it should go — either an individual project or some non-specific board for things like internal initiatives.

notes/

This is the root directory where all the text files are. I keep it in Dropbox, but it could easily be any other cloud storage provider.

notes/inbox.txt

This is a text file where I quick-capture anything I think of that needs done or needs referenced. I use the iOS app Drafts to quick append to it with a timestamp and any text I want.

notes/todo.txt

This is my low-level, nitty gritty to do list. It is granular enough to remind me exactly what I need to do. I fill and empty this list every day.

notes/work/monthly-logs/

This is a directory of text files named for each month. Each one contains a daily log of what I did, and what I need to work on next.

notes/work/clients/

Here each client I work with has a directory, with a sub-directory for each project. This is where I keep meeting notes, reference notes, project estimates — anything that I need to keep around that doesn’t fit into a task-based setting.

Workflow

At the beginning of the day I check my inboxes — email, Slack, and the inbox.txt. I follow the GTD method here, checking which items are actionable, which items need done first, and which items only need kept as reference. If something is actionable and takes only a few minutes, I do it right away. Otherwise it ends up on a list in Trello.

Trello is where tasks flow from new to complete. I use a simple structure, cribbed from examples like this one from thoughtbot. Most new tasks created from inbox items go straight to the ‘Backlog’ list.

After inboxes are organized and processed, I pick the two to three most important things I need to do — either from the Next Up list on the Trello board or existing items from the Next list in my daily log — these go into my todo.txt. Then, I pick one of those things to do. Sometimes I pick based on urgency, sometimes by which seems more fun, and often by how much uninterrupted time I have available. Keeping my todo.txt lean is important here — picking an item here must be quick. Of course, some days priorities change. Because the system is lean, I can react to that — Trello cards get bumped to a lower priority list, I leave myself a few ‘what to do when I return to this’ notes, and update my todo.txt.

When I finish a task, I make a small note in the current month’s log file. The note is simple, with minor context — the adjacent notes provide extra context. I started this habit for a few reasons. One, I need to be able to account for my time. Git commit history is a good way to look at what code I wrote or how long a specific feature took, but sometimes I need to know what I did besides write code. Two, it serves as a great way to review previous days, weeks, and months. I often end a week by looking back through these notes, and occasionally look back to what I was doing 6 months or a year ago.

At the end of the day I write down some notes on what I started but didn’t finish, and some ideas for what I should do tomorrow. I put those notes directly under my completed task list in the monthly log. This is what primarily drives my to do list for the next day. I write as much context as I need for how to resume the task. When I take the note, I am most aware of the task — and those notes let me jump right back into it. I’ve learned that this is the greatest benefit to the system — I don’t waste time trying to recall my thoughts, I can even pick a task up several days later.

Here’s an example of what an entry in this log looks like:

// notes/monthly-logs/september.markdown

# Tue 2017-09-12
- Continued work on Top Secret Application
 - Finished custom fetching logic
 - Began drag and drop sorting feature
- Meeting with Top Secret Client for Top Secret Application design review

Next:
- Update sort order on back end based on API request
- Finish UI for drag and drop

This system is tailored to my needs, but I don’t think it is so obscure that it is useless for others. I have to give much credit to David Allen’s Getting Things Done – it’s the framework for how I think about projects and tasks, everything I do in this system is a modification or elaboration on what he already made. I tried plenty of apps and combinations of apps, and only landed on this after months of trial and error. It has worked out well for me; I have more awareness of what I need to do at any time, and I can get into ‘work mode’ quicker than I used to. If you’re struggling with that, give it a try.

Estimating Client Work, and my Unexpected Buffer Time

 

Note: I use the word ‘component’ a lot in this article. By that, I mean a vague term for a small chunk of UI and functionality. Some frameworks and libraries use Component as a class, I’m not referring to any of those specifically.

When doing work for clients, you have to provide an estimate. They need to get budgets ready, plan for release dates, have people ready to use the new site or application to give feedback and report early bugs. Estimating the work that goes into a software project is, by large, a matter of experience. Over time, a developer will build up an understanding of how to guess the effort a single component will take and what effort goes into connecting that component to other parts of the application.

Once you have a model, you can decompose it into components. You’ll need to discover the mathematical rules that describe how these components interact. Sometimes a component contributes a single value that is added into the result. Some components may supply multiplying factors, while others may be more complicated.

Andrew Hunt, Dave Thomas: The Pragmatic Programmer

To estimate a component, you need to be able to see all the parts. If you can’t see all the parts, the component is too big and needs broken down. For example: a client has a document sharing application and needs to add searching functionality. That can’t be estimated with any accuracy, so we need to break it apart. What does searching include? A search form, sure. How much time does it take to add a new page, or add a field to a persistent piece of UI (like a header)? That’s one part. How much time to create a route and controller for the search form to submit data? That’s another. On and on, until each piece is broken down into a manageable chunk. This makes it easier to grasp and guess ‘roughly how much time to build this?’

Sometimes, estimating a component’s effort means actually creating a roughed out version of what you need to build. Back to the search example, if you’ve never built a filter for a list of results before, it might be a good idea to try a quick example. Don’t solve every piece of it, that’s a waste of time. But if it takes you thirty minutes to scratch out a decent working example with some static data and an ugly UI, it’s possible that thing won’t take more than a few hours total. Otherwise, if thirty minutes barely scratches the surface go ahead and assume days, not hours.

One understanding that comes with experience is the general level of complexity that the component has — and based on that level of complexity, how much extra time needs factored in. When I build estimates, I call this my “unexpected buffer” time. Back to the search filter example again: The client is fine with a full page refresh whenever some new filters are added. Simple to build, not a lot of complexity. Now, let’s assume the filtering and sorting needs to be instant, but that entire search result needs to have a shareable URL. This is a level of complexity that can introduce unforeseen problems. We still give both components an estimate according to the effort — but each one also receives a buffer time. Low complexity, add 10% to the estimate. Maybe 20%. Highly complex, add 50%. I’ve gone so far as to double my estimates.

The buffer is the unsung hero of my estimating process. It keeps launches on time and saves everyone’s sanity. And, if you don’t use it — your client gets an early delivery, which I guarantee they will love.