Engineering Tenets: Mastering Competency
In our new 4-part series, Director of Engineering Spencer Norman explores the ways in which Reaction Commerce’s engineering tenets lay the foundation for a successful technology-first organization. Each week, Spencer will dive into one of our four overarching themes—communication, character, competence, and cooperation—along with the core values that drive each principle.
We get excited by the prospect of working alongside people who are among the best at what they do, regardless of what team they’re on. When teammates deliver on their commitments, we’re able to focus better on our work. The trust we have in each other is rooted in the reassurance that each and every one of our teammates exude competence.
Competence is generally defined as the ability to complete a task successfully or efficiently. But how do we, as a team, define competence? In this blog post, we’ll explore some of the values that make up this definition: critical thinking, excellence, consistency, and precision.
Developers, programmers, coders, and engineers—lots of words for the same essential job. We use the term Software Engineer here, partially because the main responsibility for this role is to solve problems rather than write code. True, writing code is how we go about solving problems, but writing code isn’t the point. The solution is the point. Because of this, we believe that critical thinking—thinking for yourself—is a fundamental characteristic of a great engineer.
We consider personal thinking to be a major part of critical thinking. Personal thinking means not shying away from disagreement, even if we have enormous respect for the person we’re disagreeing with. It means questioning whether the way we’ve always done things is the best way. It means making time to focus and think—not build, implement, or respond. As a team, we place value on the intersection of personal and critical thinking to avoid groupthink and confirmation bias.
A part of critical thinking means identifying the right problems to solve. Frequently, this means not jumping straight into code when approaching a feature, issue, or bug. Solving problems means making compromises and being intentional about the trade-offs. Additionally, it’s not enough to simply solve the initial problem—we must also make sure our code isn’t creating any new problems. We expect engineers to identify and anticipate the second and third-order consequences of their work before code review. This takes extra time and focus on the author’s part, but it saves the team a lot of time in the long run.
Often, just by spending time writing down anticipated consequences of the code, a new solution—one without as many unintended side effects—will reveal itself. It takes critical thinking to communicate what we’re working on to our peers, and that can be a fantastic way to perform a review on our own code. By critically approaching our own work before handing it off, we can reduce the amount of time we spend reviewing code, thereby increasing our velocity as a team without sacrificing code quality.
Here’s a question I occasionally ask during one-on-ones: “Are you doing the best work of your life?”
Do the best work of your life—that’s a pretty heavy expectation. But we aren’t going to change an entire industry just by doing an adequate job. In my last post on character, I discussed the importance of fostering a growth mindset. This plays directly into the expectation of excellence. Will our teammates do the best work of their lives every day? Probably not. But we do expect that 6 months from now, they’ll be working smarter and leveraging new skills. Most importantly, they’ll know more now than they did the day, week, or month before.
It’s difficult to do the best work of your life if you’re not consistently being challenged to do things that are on the edge of your capabilities. Of course, this could be the result of many factors: a lack of challenge, too much challenge, not enough mentorship, or a combination of things. A good team, organizational, and cultural fit often serves as a catalyst for excelling. When someone is not doing the best work of their life, it’s an indicator that there might be bigger problems at play.
As an organization, we aim for consistency: consistent releases of code using defined, systematic processes, including documentation with a unified voice. Creating consistent output is an outcome of a team full of consistent people. Our desire is to always be shipping. When we resolve an issue or add a new feature, there’s no arbitrary delay once it’s ready. It should get shipped out as soon as it’s been reviewed and approved.
Consistency and frequency may not be the same thing, but we do see a correlation between the two. We commit early and often. We keep each chunk of code focused on doing one thing well. We aim for atomic-ish commits, checking in on code as frequently as possible. As a result, we’re able to check an active branch at any point and see the latest version of the code, rather than just one or two commits. Code reviews are easier this way, and when future issues arise, it’s simple to identify when and where they might have formed.
In addition to automated tests, we also leverage automated tooling to keep our codebase clean. We use ESLint, jsdoc, and inline comments to enforce a consistent style and voice within the code. As we run into issues that haven’t yet been defined in our style guide, we work to document the patterns that arise.
At Reaction, we aim to to move fast, continually build, and release trusted, reliable, high-quality software for retailers around the world. Doing things right the first time empowers us to release frequently and keep the quality high. Along with solving the right problem, it also allows us to spend a little more time thinking before starting to write code (see also: hammock-driven development). This extra time—mostly spent on architecture and engineering—results in a greater product overall.
Doing things with precision means that we start with goals, defined by a series of tests and product specifications. Then, we write code that fulfills these goals. We spend time documenting exactly how the code works, how it should be tested, what other solutions were considered, and why we ultimately arrived at the solution.
In an ideal world, we’d start by writing user docs as feature specs. We’d write tests to meet those needs, write code to pass the tests, then document the code. Docs => tests => code. We’re not quite there yet, but by building a precision-based culture—a culture focused on documentation and testing—we’ll get there soon.
Interested in joining the Reaction team? We're hiring! Check out our careers page for more info.