Angelo Stavrow [dot] Blog

Missives and musings on a variety of topics.

This week, I took a hiatus from the refactoring fun I've undertaken to (finally) move the blog over to a static site generator.

I've written before about my plans to move to Hugo, and that's now complete. Since the beginning of the year, all posts have been written as Markdown files that I can easily grab and format with the necessary front matter. Before then, I hadn't written very much, and in fact I've dropped a couple of old posts that I felt didn't add to the site in any way.

The site-generation files exist in a GitLab repo. I treat this site as I would any other software project; issues are opened to add or modify things (including posts), a branch is created to address these issues, and when the work is done, the changes are merged into master.

Whenever something happens on master, GitLab's CI kicks in and a shared runner picks up the job, builds the site, and uploads the public/ folder to GitLab Pages. This is one of the easiest ways to set up continuous deployment for the site, but when I started trying it out there was an open issue to resolve for performance—it could take hours before the changes went live. That's gotten much better since GitLab 8.9RC3 was deployed, and I only make one post per week so it's not that big a deal—but if you need to keep things up-to-the-minute fresh it's not a bad idea to set up a project-specific runner instead to build and deploy your site.

The official deploy date of the site is—as you can see from the 1.0 milestone—Friday, July 1st.

One more thing: the RSS feed for this site will be changing. If you subscribe to the feed, please update your link to

http://makebeforebreak.com/index.xml

More tk!

Discuss...

I'm in the process of (finally) finalizing the move of this site to Hugo.

No, for real.

Most content is already in place. A few older posts need to be added, and some info pages are to be added. This is normal-priority.

I'm using a slightly-modified built-in template to render the site. There a few more modifications to make, but this is low-priority.

A commenting platform will be added. This is also normal-priority.

I want to explore adding SSL to the site, but this is low-priority.

Finally, a deploy strategy is to be set up—this is high-priority. The plan right now is to have the following workflow:

  1. Merge in some changes (i.e. New post, updating old content, &cet.);
  2. Merge commit triggers CI to build the site;
  3. When CI succeeds, deploy the built site to the web host.

This will give me the opportunity to post from either my computer or my phone without issue. Then it's just a question of reconfiguring DNS to serve up the new site.

If all goes well, next week's post should be on the new platform!

Discuss...

The Great HoneyJar Refactoring is a series of posts in which I take the first iOS app I ever wrote, HoneyJar, and refactor it out of its original burning-dumpster-fire state and into a modern app. And I'm doing it in public.

Earlier this week, I tweeted about my adventures in trying to add a test suite to HoneyJar.

The idea is this: I want to be sure that I'm not breaking anything in the app, as it exists right now, when I start refactoring. Without an existing test suite, I have no way of knowing if I'm creating any regressions.

The TDD approach is to

  1. Write a test that checks a particular public method against a particular condition;
  2. Write the code that makes the test pass; and
  3. Repeat steps 1 and 2 until you're done writing the method / class / app.

The tests come first, and the code “fills in the blanks”.

When you've got a legacy codebase1, however, does it still make sense to write failing tests first? Probably not.

Furthermore, what if it's just not possible to test certain methods or classes, because of the way the code is structured? If you haven't written the code with testing in mind, then it might be too coupled or complex to test after the fact.

Stop writing legacy code. Do not write new features without unit testing. You are just making the code base worse and getting further away from introducing tests into your system. Get your testing infrastructure set up and a few tests running successfully and it will be much easier to think about introducing tests for the rest of the code.

— Dan Lee, TDD when up to your neck in legacy code

Right now, what's making me a little bit crazy is that I'll open a class to start adding some unit tests, see all kinds of ways to improve the code before adding the tests, and have to remind myself that I've made a promise not to touch the code until the test suite is in place.

SO. FRUSTRATING.

It's especially frustrating when you realize that you'd written something that you can't really test, but you're not allowing yourself to fix it.

But we have a great way of getting around that. Comments.

So, here's what I'm doing:

  1. Open the class to which I want to add tests;
  2. Add a new test case class for said tests;
  3. Scan through the class under test and grind my teeth at obvious problems;
  4. Take three deep breaths;
  5. Start adding whatever tests that I can add without changing the code; and
  6. Add thorough //TODO comments on “next steps” for refactoring this class—obvious problems, how to improve testability, &cet.

I'm going to go through doing this for the entire codebase first. I could add some tests for a class, then refactor it, then move on to the next one, but then I have no way of knowing if I'm making a change that will somehow propagate through the app and trigger a failure elsewhere. So, while I'm not especially happy with the coverage I'm getting right now, it's better this way.

Once the issue is closed, I'll start tackling the actual refactoring—and I'll feel more like the project is ready for others to work on, too. For me, at least, I t's really hard to make changes to a codebase I'm unfamiliar with if I don't have the safety net of a test suite.

As always, more tk.

  1. By "legacy codebase", here, we mean untested code, i.e., a codebase with no pre-existing test suite.

#tghr

Discuss...

The Great HoneyJar Refactoring is a series of posts in which I take the first iOS app I ever wrote, HoneyJar, and refactor it out of its original burning-dumpster-fire state and into a modern app. And I'm doing it in public.

Last week I introduced an idea I had: to open-source my first-ever iOS app, embarrassing as it might be, and refactor it out in the open.

Over the last week, I handled the open-sourcing of the app. This took a bit more time than I expected, and normally this should be done before releasing the project to the wild, but I figured it might be interesting to see just what the process was, what I learned, and some resources I found along the way.

The starting point

HoneyJar, in its closed-source state, was very clearly an “I'm learning as I go and am super excited to ship” kind of project. It had a README, kinda, which really just re-iterated some tagline-style description of the project, and that's it. Unit tests were few and far between, comments were… not awesome, and there was no real structure in place to make it useful for other contributors.

So the first step was to do exactly that. I opened the first issue and set up a task list:

  • Add a CONTRIBUTING document
  • Add a LICENSE document
  • Add a CHANGELOG document
  • Update the README to provide more information on the project
  • Set up GitLab continuous integration

Okay. I know. That sounds like a pretty straightforward list. Quick and easy to add. Turns out, you should put a little effort into these things.

Readez-moi

The first thing most people will see in a project is the README file. This should provide some information on how to install/use the project, where to find further info on licensing and contributing, &cet. But how much info? What exactly do I need to add?

I found an excellent template by Billie Thompson that fits the bill nicely. While I've commented out a bunch of stuff that isn't necessarily relevant (or maybe not relevant yet), I think it provides a pretty good overview of the what, who, why, and hows of the project. I'm relatively happy with the way the README turned out.

With every change, log, log, log

Next up: everyone's favourite, the CHANGELOG.

No, seriously. This either gets totally overlooked, or totally becomes a series of punchlines about episodes of Friends or whatever. In trying to figure out what needs to go into a change log, I discovered Keep A CHANGLELOG. Great resource.

As you go through merging in changes, part of the merge should including updating the change log with whatever you've, well, changed. As the project maintainers indicate, typical sections are:

  • Added for new features.
  • Changed for changes in existing functionality.
  • Deprecated for once-stable features removed in upcoming releases.
  • Removed for deprecated features removed in this release.
  • Fixed for any bug fixes.
  • Security to invite users to upgrade in case of vulnerabilities.

Additionally—I love this— every subtitle of the document should be a link to a git compare of the previous and current tags (or HEAD, if you're working on an unreleased version).

Excellent idea.

License to thrive

Jeff Atwood said it in 5 words. Pick a license. This is important, or you're pretty much putting your work out there without letting anyone know if they can use it. Picking a license helps you both cut the overhead of having to reply to emails (“can I use this code?”) while putting down some ground rules on how your work can be used.

I went with a simple 3-clause BSD license. Essentially, this is a use-as-is-without-warranty license, requiring attribution by others who redistribute your work, and written permission if they want to use my name (or my organization's name) in any endorsement or promotion of their derived products.

Just add contributors

The hardest to set up was the CONTRIBUTING document. Here, there are two main things I wanted to address:

  1. Expectations of all involved (i.e., a code of conduct), and
  2. Just how to contribute.

The how-to-contribute part is pretty straightforward. Everything should start with an issue, and once labels and owners are assigned, work can begin. There's some basic info on how to report a bug vs. how to add a feature request, and I added some requirements for merge requests too. Such requirements can get pretty complex and involved, but it's important to step back and examine the scope of the project too. We're not building CocoaPods here; a tiny not-super-important project like HoneyJar probably won't attract much in the way of contributions, so there's no need for overly complex requirements for making changes.

That said, I believe that any project, regardless of size, has the potential for community. And a community is only as good as the expectations for interaction. In my work, as in my life, I really try to come from a position of trust, mindfulness, and open-ness, so putting together a code of conduct that embraces these values is important to me.

Luckily, I'm not the only one that feels this way. The Contributor Covenant is a good place to start, and other open-source communities (1, 2, 3, as examples) have done a great job of adapting it. I've followed in their footsteps, and I think the code of conduct section nicely explains the let's-be-wonderful-to-each-other feels that I want in my community.

All the great fails

The last thing I absolutely wanted up and running for this first issue was to get continuous integration up and running.

“But Angelo, you don't have a test suite set up for your project.”

Yeah, I know. But the next big step before I start working on the actual refactoring is exactly that: add a test suite for existing code. It would be downright silly to try refactoring something without making sure that you're not breaking anything as you go through.

So, I followed the steps in the article I wrote and created a new runner and a .gitlab-ci.yml file, along with some sample test, to make sure everything's working.

And builds kept getting stuck with the error:

Failed to authorize rights (0x1) with status: -60007.

Turns out, I needed to update the runner on my build machine to address some changes in Xcode 7.3—once that was done, things started building as expected. One of the great things about GitLab being an open-source project is that if you're running into trouble, there's likely an issue available to help you figure out what's happening and how to fix it (and if there isn't, open one!).

I've left the build history as-is, along with the commit history, as breadcrumbs to how I managed to get things working, along with the stupid mistakes like overlooked typos in your runner tags. I don't think there's any value in trying to hide these goofs, because they could be invaluable the next time you're yelling at your system for not just working come on you stupid thing everything was fine a minute ago what do you mean this tag doesn't exist.

Onwards and upwards

So, the project is now, IMO, officially open-sourced. The next big step is to add the test suite, and then I can move on to the fun stuff: refactoring.

More tk.

#tghr

Discuss...

Over the next few weeks, I've decided to write a series of posts about taking my first iOS app, HoneyJar, and refactoring it from its current (terrible) state into something, well, less-terrible.

And I plan on doing it in public, too.

I'm calling this project The Great HoneyJar Refactoring.

HoneyJar is a future-value calculator. As explained on the product page, you set a payment amount, a rate of return, and a period of time, and HoneyJar will give you a scenario showing how your money will grow if the amount entered amount was

  • a one-time lump-sum payment
  • a series of equal weekly payments
  • a series of equal monthly payments

I haven't really touched it after I released 1.0 save for a few minor updates. It could certainly use some love, as there's a whole lot of technical debt going on in that codebase. Just off the top of my head, without having poked around the repository for months, I can list the following required fixes:

  1. Refactor the main UIViewController into several lighter classes. The thing is a mess.
  2. Along a similar line, convert the project to use Storyboards and Autolayout.
  3. Add a test suite.
  4. Add localization (I'll start with English and French, since I speak those languages pretty fluently).
  5. Ensure accessibility.
  6. Port the thing to Swift, or at least write new code in Swift.
  7. General UI/UX improvements (e.g., make the result presentation clearer).
  8. Drop support for devices running iOS 8 and before.

I'm going to call this version 2.0 and I've opened a milestone in GitLab to track my progress. The associated issues will be added over the next day or two, so you can see how what I'm working on at any given time. I'll also be setting up CI and other (non-code) project settings.

So, I'm open-sourcing the (embarrassing) version as it exists right now—please be nice—and I plan on tackling one issue every couple of weeks or so, until the milestone is closed and 2.0 is released. Each time I close an issue, I'll write up a post on the whys and wherefores of the changes.

I haven't yet decided if I'll accept any merge requests during this project, but I certainly look forward to feedback on my work. To make it easier for people, to follow along, I will be mirroring the repository on GitHub, too—GitLab makes it easy to push changes to a remote repo. GitLab is my origin of choice, though, thanks to the built-in continuous integration.

Update (2016-05-25): I've moved the GitHub mirror from my personal account to the organization account, since it's Dropped Bits that's handling the App Store stuff.

More tk!

#tghr

Discuss...

I mentioned that keeping comments up-to-date with modifications in source code helps me with task-switching in a busy workplace.

Of course, that doesn't make a difference if you're not commenting your code well.

Therein lies the rub. I spent weeks on learning various sorting algorithms in my data structures and algorithms class, but I never really learned anything on the whys and wherefores of commenting.

I've been taught that

// This is an inline comment.

and that

/* This is a block comment, which makes
 * longer comments a little bit more
 * readable.
 */

and also how to use comments to temporarily disable portions of your source code to try something out.

And that's fine.

But what should you put in your comments?

Visual Studio has a neat feature where if you type three slashes in a row (///) before a method definition, it'll expand a comment snippet that includes, as appropriate, sections for a summary, what method argument parameters are, and what the method returns. Whatever you enter there shows up in a tool tip when you hover over the method call later.

Similarly, three slashes (///) in Xcode will allow you to enter comments that show up when you hover over method calls.1

Handy, for sure.

But, really, when I'm heads down and trying to push code out before, say, I leave for the day, I've noticed that all I write in these comments is a re-iteration of the method signature—and given that I try to use very descriptive method and variable names in the first place, these extra lines of typing are pretty redundant. I'm just giving myself work for zero added benefit.

Samantha's tweet inspired me to think a bit more about how I comment my code. Intent is key, I agree. If the source code is the documentation2, then the comments should probably play the role of both the chapter summaries, and the sidebars that warn of the gotchas, the perils, and the pitfalls. The bigger-picture stuff. Why is this structured this way? What might I have to look out for when testing this particular method?

All the best practices we learn—design patterns, unit testing, &cet.—are meant to make our code more maintainable. Comments should be an integral part of that, not just as a repetition of what's obvious from the source, but as a reflection of the intent of the thing.

  1. You definitely should pick up Erica Sadun's Swift Documentation Markup to really make the most of this feature, by the way.

  2. I'm not 100% convinced of this particular mantra. Of course, code is the canonical source of ground truth, and that's the intent, but as far as it being documentation—well, that's a post for another day.

Discuss...

I've discussed the transition over to a static-site generator before.

Yes, I'm still planning on doing this.

Yes, it's been a long time coming.

So I'm setting a deadline for myself: the next post on this blog will be with the new setup. It probably won't have all the pieces in place re: design and other related-site plans, but I've got to start somewhere.

More tk.

Discuss...

As the sole developer at work, I have to balance our “ask me anything, anytime” culture with a need for unbroken concentration on whatever code I'm writing.

I'm generally fine with a door-always-open policy like this, because it makes communication fast and easy—especially in a small team, where everyone wears several hats—but as projects get closer to a deadline, it does tend to put a bit of strain on my ability to get things done. When that project involves a relatively complex application, however, it can be very hard to keep things on schedule, because task switching has such a high cost:

That's because programming is the kind of task where you have to keep a lot of things in your head at once. The more things you remember at once, the more productive you are at programming.

I've been struggling with this a little bit lately. It's no one's fault, of course—colleagues are just interacting with each other the way they always have.

So far, I've been trying some of the more common workarounds to this kind of workflow:

  • coming in to work two hours earlier than anyone else to focus on trickier problems
  • working from home1
  • declining meeting requests that I don't absolutely need to join
  • delegating other tasks to other colleagues
  • using noise-attenuating headphones when I need peak focus

It's helping somewhat, but what I've noticed as the project approaches release-candidate is a little more interesting.

The more loosely-coupled the code is, the easier it is to come back to it after an interruption. Instead of having to keep all kinds of interaction in my head, I love small, simple classes that have no knowledge of their context. They do one or two related things, and that's it. Jumping in and out of these is relatively easy.

A good debugger and a test suite are indispensible. Interrupted tasks are twice as likely to contain errors, so a thorough test suite—and really writing tests before code, as TDD requires—helps catch these. Some parts of my project include firmware that's written without the benefits of test frameworks or breakpoints, so I have to resort to caveman debugging. This is something I'm going to avoid in future projects.

Refactor your comments with your code. You should never trust your comments to accurately reflect what your code does unless you consider them to be inseparable from the code, and change them while you make changes to your code. When you're interrupted, ask for a minute to take a note on what you're doing and add TODO:, FIXME:, or HAX: comments that you can come back to.

These things do way more for my ability to mitigate how badly an interruption affects my productivity than headphones, working from home, or refusing meetings, and they make my code better to boot.

What works best for you?

  1. This is difficult, because the software I’m working on has to control some permanently-installed hardware in our lab.

Discuss...

This is the 17th post made to this blog since the beginning of 2016.

That means that I've posted something every Friday since the start of the year.

Yesterday, I suggested that I might not be posting anything today. I've been feeling a bit tired lately as deadlines loom for work projects, so personal side-projects tend to slip a bit.

But, dammit, the idea of not breaking a streak—especially four months in—really does work as a motivator to keep things going. This may be far from a daily habit, and I imagine most people wouldn't notice if I missed a week, but I just couldn't bring myself to leave a gap in my posts.

Because that's how it starts for me—skip one entry, and then I've subconsciously given myself permission to drop the project entirely.

It's okay to drop projects, but I feel that should be a conscious decision made when you're taking stock of what's going on in the entirety of your life, not some subconscious decision from a contextless state of mind.

So, yeah. Post number seventeen. Streaks work.

Discuss...

A short post today, based on a realization I had earlier this week at CocoaHeads.

Like many, I often suffer from impostor syndrome. It hits when I'm tackling something new that I thought built on previous knowledge, leaving me feeling a bit bewildered, and undermining my confidence in having mastered lesser matters.

Usually, I take a walk and come back to the problem with a clearer head, and will work my way through it.

Given that I've always worked as a solo developer, with no formal computer science or software engineering background, I've always felt a bit iffy about my programming chops. I do my best to emulate what I see, but sometimes can't shake the feeling that the code I write is amateur and inelegant.

I look back at old code and think it's garbage. This should probably be seen as an indicator that I'm progressing, but it sometimes just reinforces the “you're a hack” feelings.

But Tuesday, looking at some of the code presented in the demos, I realized that people were writing code the same way I would have written it. Maybe some stylistic differences, sure, but generally, it turns out that I'm at a point where my approach to writing code is getting there.

I guess the point is that, eventually, you get there. A year ago, if I had to show code to someone, I would have been making all manner of apologies for it. While I'm still pretty far from mastery, it's reassuring to see the improvement.

So, you know. Just keep swimming.

Discuss...

Enter your email to subscribe to updates.