Gut Renovation

Brian W. Wolter
Hirepurpose
Published in
4 min readJan 31, 2017

We’ve gotten a lot of mileage out of the first version of the Hirepurpose jobs platform. Our flagship product began life as a monolithic Rails app running on Heroku, which can be a great way to get a project up and running with minimal effort – in no small part because most of the basic engineering decisions have already been made for you.

However, after a couple years of iteration, refactoring, a growing user-base and increasing business complexity, the cracks had become impossible to ignore. By mid-2016 it had become clear that we were building on a foundation that wouldn’t support our ambitions.

Our inflexible, old architecture.

Services, Of Course

We set out to re-architect our product as a collection of well-defined, containerized, horizontally scaleable services with clear separations of concern. Each would be exposed (at least internally) via an API (and sometimes a queue).

Service-oriented platforms promote flexibility and autonomy, two characteristics we would very much like to encourage. They empower individual component teams to leverage existing functionality in the broader system and simultaneously to contribute back to the expanding collective utility of that system.

By designing around this architecture we can build new products and features composed of functionality from existing services and do so without having to explicitly coordinate our efforts across teams. We can move faster more easily and duplicate less effort.

With these principles in mind, we set out to lay the foundation of our new platform. Over the course of a few weeks in August the design we ultimately arrived at (illustrated below) began to take shape.

Shiny new architecture

Now, in comparing the considerably more abundant boxes and arrows in this new architecture to our box-and-arrow adverse original architecture one could be forgiven for concluding that this new design would actually create complexity, encumber our team and reduce productivity.

In practice, however, that’s not so. From the perspective of the teams working on individual products and features, this new architecture is dramatically simpler and easier to reason about.

Sure, there are many more components which must be managed, but that’s DevOps’ and AWS’ problem. Individual teams are free to focus on the problem they’re solving without worrying about the underlying programming languages, frameworks, data stores, or other implementation choices made by the services they integrate with. They only deal with well-documented service APIs.

What’s not to love?

Radical Change, Eventually

With our new architecture’s blueprints in hand, we knew exactly how we wanted our system to look. So it was time to tackle the bigger problem: how to get from where we were to there.

As faithful adherents of the First Commandment of Software Engineering, rewriting from scratch was never really an option. Our approach, instead, has been to incrementally transition specific functionality from our Rails app to standalone services as those components needed updates. This has allowed us to gradually migrate our system piece by piece — and to realize real benefits from our new architecture — without huge lead times up front.

We’ve adopted a general methodology which guides the process of upgrading and service-izing functionality:

  1. Identify existing functionality that needs improvement — or new functionality that depends on existing components,
  2. Determine the subset of existing functionality that needs to be migrated to the new service in order to support this functionality,
  3. Design and build the new functionality — and the subset of migrated functionality — in a service component,
  4. Migrate away from our legacy implementation to the new, improved service-backed implementation.

After enough iterations of this process, slowly chipping away functionality, all business logic and all interaction with storage will be handled exclusively by services and orchestrated by an API layer. We’ll be living the dream!

Defensive Upgrades

At present, we’re already several iterations into this process — and we’ve certainly had to file down a few sharp edges along the way. A complete refactoring of any system is bound to present unanticipated challenges, so it bears keeping in mind that a bit of prevention goes a long way.

We’ve been able to greatly reduce the risk associated with rebuilding critical systems by first deploying our new services in parallel with the legacy functionality they will replace. This approach makes it simple to gradually roll out updates to our users — and to quickly roll changes back should anything go haywire.

Metaphorically, we build the new bridge next to the old one and then, once it’s complete, gradually route traffic to it. This is a pretty well-established pattern used by larger companies to roll out new features. The inexpensive and immediate availability of compute resources on AWS (and elsewhere) means there’s really no good reason for a smaller company like us not to take this precaution as well.

Onward and Upward

We’ve still got a long row to hoe before our master plan is fully realized, but really it’s just a matter of time. The best way to set yourself up for success when undertaking a long-term, large-scale project is to invest in developing (and refining) a process.

After a few rounds of designing, testing, tweaking and polishing the process outlined in this article we’re feeling pretty good about the future of Hirepurpose’s system architecture — and everything we’ve got plans to build on top of it.

Hirepurpose connects Fortune 1000 companies with military-experienced talent. We’re hiring — help us build technology that empowers the next generation of American veterans.

--

--