Engineering Principles at Code for America
A couple months back I wrote about our next chapter at Code for America: We’re demonstrating impact at scale by delivering digital services that just work for people who need them most.
But how do we go about designing and delivering exceptional digital services? It starts, as our own Dave Guarino would say, with binding ourselves tightly to the real needs and problems that people face. With that as a starting point, we’ve laid out a set of principles that guide us as we build digital services at Code for America. I’d like to share a lot more detail on those principles today.
We do our work by focusing on Building the Right Thing, Building the Thing Right, and Being a Good Teammate.
Building the Right Thing
Understand User Needs
First and foremost, we talk to users and understand who they are. We’ve found that much of the time the best way to get started is not to write any code, but instead to just help people, manually. Once we have a live service, we make sure to insert a “seam” such as live chat that allows us to talk to users regularly.
Define Your North Star
Just as an organization exists to serve a mission, a service exists to meet a public need. Each of our services has a North Star Metric that tells our teams most clearly the need is being fulfilled. It is the one metric, above all else, that guides us as we do the difficult work of prioritizing all the problems we could be solving. For example, our North Star for GetCalFresh is closing the SNAP participation gap in California.
Chart Your Course
Agreement on what we’re trying to accomplish, how we’re going to do it, and how we’ll measure our progress helps us chart a course towards our North Star. To that end, each team at Code for America meets quarterly to do a deep dive on strategy, objectives, and measures (we use OKRs) that help to focus and guide us for the coming three months.
Dwight D. Eisenhower famously said, “No battle was ever won according to plan, but no battle was ever won without one.” We couldn’t agree more. The OKRs and roadmaps we produce are helpful as guides, but the landscape is certain to change as we iterate and learn throughout the quarter. That’s OK! The discussions we have as a team to come up with them are invaluable.
Optimize for Speed of Learning
Armed with a clear North Star and a plan to reach it, we can focus on possibly the most important part of our process — learning at the fastest rate possible. We’re big fans of the Lean Startup approach and we’re always trying to accelerate the speed at which we can get through Build-Measure-Learn loops.
Don’t Argue — Ship!
Optimizing for speed of learning means we’re also optimizing for speed of shipping, and that means keeping the cost of shipping low. Once you’ve done that, shipping to get high-quality evidence becomes the easiest way to arbitrate disagreements, and that’s a beautiful thing. When our teams disagree about an assumption, we stop arguing about what to do and start figuring out how to ship code and gather data to aid in the decision.
Building the Thing Right
Make Pragmatic, Goal-focused Technology Decisions
We always make sure we have a pragmatic rationale for our choice of tech that maps to our north star or enables us to learn and ship faster.
For example, many of the problems we work on are best solved with a straightforward CRUD app. Ruby on Rails has great, mature tooling for building CRUD apps, and is our web framework of choice.
We try hard not to reinvent the wheel in other areas as well. We want our teams to focus on meeting user needs and not on building tools that others have done well already. We use Twilio for text messaging, Intercom for live chat, and many other services that help us keep that focus.
Build Visibility In
As we optimize for speed of learning, it’s also critical to optimize for the speed at which we can answer questions to inform future development. We make sure to track metrics around all of the important user interactions that we use to measure success. We also ensure that it’s easy to add new metrics, to keep the cost of evaluating a test or new feature low.
Similarly, we make sure we have good visibility into problems users may be having with our services. Quality monitoring is key when it comes to providing a high quality service to our customers.
Write Quality Code
What makes for good code can be a pretty subjective topic, so for our purposes we define quality code specifically as code that has been peer reviewed and is covered by automated tests.
Code reviews are a must for maintaining high-quality, readable code. They are also a great way to spread knowledge among teammates. Every team at Code for America requires a code review before any code can be pushed to production. Some teams use a Github pull request model, others prefer pair programming. Either way, we always have a fresh pair of eyes on our code.
Writing automated tests is perhaps the most important thing we do to assure the longevity of our work. We’ve found it to be the best way to get a high-quality code base and maintain velocity. Tests reduce the cost of change as our code base grows, empower us to refactor with confidence, allow new developers to contribute more easily, document our code’s behavior, and make finding and fixing bugs significantly easier.
Keep the Cost of Shipping Low
Given that our speed of learning is largely dependent on our speed of shipping software, we make sure to build our services such that shipping software is a non-event.
In addition to building in visibility and writing quality code, we make sure every project at Code for America has continuous integration to run tests and inspect code for quality and security. We keep our changes small, our deploys simple and fast, and we treat our servers as cattle not pets.
Learn from Failure
Complex systems fail. This is unavoidable — but we can learn from our failures and iteratively make our systems more resilient.
Monitoring and logging are important for identifying and troubleshooting failures, but probably the most important part of this at Code for America is the blameless post mortem. We are committed to fostering a just culture so people are empowered to address the systemic issues that lead to failure.
Being a Good Teammate
Earn & Give Trust
Developing trust is a necessary first step for any high-performance team. At Code for America, we all show up every day for the same reason — to help government do a better job of serving people in need. We trust each other to do everything we can each day in service of that goal. It’s up to each of us to live up to that trust and deliver on our commitments.
Disagree and Commit
This is an oldie but goodie. We have a responsibility to ourselves, our teammates, and to the people we serve to voice our disagreements. Once a decision is made though, we commit to implementing it, in an iterative, data-driven fashion, so the next decision we make is based on a deeper understanding of the problem we are solving.
We are all accountable for the work that we commit to — we own our goals, our actions, our approach, and most importantly our mistakes. It’s up to each of us to drive the change we want to see.
We’re building for real people who rely on our products and services to live a better life. Every action we take should be focused on delivering results so we can better serve them.
Fix Problems, Even When They’re Not Yours
However strong our practices and however good our intentions, mistakes will happen and problems will arise. When we encounter a problem, we take ownership of it and fix it. When we work on a piece of old code, we leave it better than we found it. We write tests if they’re missing. We make code more readable if it was hard to understand. We all have a shared responsibility for the quality of our services, and it’s up to all of us to safeguard it.
These are the principles that guide us as we strive to improve how government delivers services through technology. If our approach resonates with you, take a look at our open positions. We’re hiring!