Engineering Culture at MasterHealth
Joining a new team is an exciting opportunity and a cautious endeavor. You don’t quite know how your teammates work, what they expect from you, or how they’ll react to your contributions. This can also be your chance to shape how things are done based on your own experiences. Discussing “team norms” has been a popular concept for newly formed and growing technology teams. It means that expectations are laid out collaboratively and we do our best to follow them. Working relationships thrive when we agree to best practices together, and talk about and update them regularly. We’re sharing this to help you learn what it’s like to work with us and build comfort quickly when you join MasterHealth.
- This is a living document: we’ll update it as we learn, grow, and bring on new team members with their own ideas on how to be an effective team.
- We’re weaving in examples to help illustrate our principles. Keep in mind that the specifics don’t matter as much as the overarching aim.
It’s important to take breaks throughout the day to get a sense of what our teammates are working on. While we value flow & focused work, we know that beyond shipping our own code, the following kinds of contributions are equally important:
- Providing feedback on code ready for review or green-lighting a PR
- Reading & providing feedback on a tech spec or Request for Comment
- Keeping on top of what our teammates are planning to work on and proactively helping with potential roadblocks
- Chipping in on Quality Assurance for a teammate’s contribution if you think you have more context or suspect an edge case was missed
Software is a living thing, constantly evolving along with its requirements. Team members are often deep into very different business goals or technical problems, but we still want to collaborate across the things we individually own so that we’re benefiting from each other’s experience & insight. We think about our teammates by providing ample context:
- In PR descriptions we’re clear on the:
- Why: a description of the problem we’re solving with a link to relevant tech specs or business requirements
- What: pointing out the most important changes being made or the rationale for an approach that was taken
- How we tested: detailing the specific scenarios and edge-cases we tried to account for, along with the method we used to test (ie: the user state we had to be in, the platform or environment setup required)
- We recognize that future teammates may need to change the code we write or understand the decisions we made. We understand that our PR is a historical artifact of our contribution
- In shipping our change we’re clear to stakeholders about what they should expect:
- Which build or release the change will be in
- Whether the change is “turned on” or will only be visible in certain situations
- How we’re monitoring for performance if needed
- We offer to pair on more complicated code reviews or provide a video screencast overview of the change if needed
- We recognize that submitting a PR is a call for help, and we should make it as easy as possible for our teammates to gather the context we had in producing that code so that they can be effective in their review
- Providing ample context can take more effort than the code change itself, and that’s fine!
We know that code reviews are not the place for stylistic feedback. Especially for new developers to the team, an unfamiliar codebase can be a contradicting mash of different patterns and styles. What’s most important is that their contribution achieves the UX goal, bug fix, or business requirement in a way that’s easy to understand for future developers. We tackle style issues by:
- Starting conversations about code formatting, style, or patterns in slack or on a call and codifying those in a style guide (if they can’t be enforced automatically with a linter)
- Retroactively applying new style guidance elsewhere in the codebase for consistency
- Noting in the PR that this comment is “not a blocker” to it merging and can be a follow-up PR
We believe that non-draft PRs open for more than a few days are a sign that something is wrong, potentially with one of the above principles. We recognize that it’s important for an early-stage startup to iterate on and learn from user feedback quickly to remain viable. This means shipping code! We employ some of the following tactics to do so:
- We aim for small, cohesive changes and employ squash commits on PR merge for that reason
- We leverage build flags or feature toggles to constantly deploy incremental changes to live code until we’re ready to enable the larger feature that those individual commits amount to
- We sometimes merge without waiting for a review, especially for hotfixes
- We review PRs regardless of their merge state – merged code can be amended or feedback addressed in a follow-up, and as the author we take that feedback just as seriously
We don’t have specific roles for QA and won’t in the future. It’s expected that when a PR is merged it’s been adequately tested & verified post-deploy by the author. At that point, it’s likely been reviewed by other team members who have also considered the test plan and done their own QA (if the change merits one). Deployment doesn’t mark the end of an author’s responsibility for their change: it’s expected that the deployed change be verified as soon as possible so that a rollback or patch can be made at the first sign of trouble. Having a plan & responding quickly to remediate any issues that may arise, before we hear about them from our users, is just as important as the pre-deploy QA itself.
Every day we try to make pragmatic trade-offs between addressing every edge case or requirement and shipping code early & often. Sometimes that means taking manageable risks, especially in more experimental stages. Risks can come in many forms, including to:
- service availability (a known performance bottleneck)
- release stability (code that’s hard to maintain or test)
- brand reputation (a data breach at a third-party analytics provider)
- security (deferring the implementation of two-factor authentication)
We proactively acknowledge the risks we’re taking so that we can effectively plan to mitigate them as a team by:
- Documenting the risks we’re taking, assessing their severity, and sharing our near term and long term plans for mitigation
- Instrumenting alerts or monitoring as needed for expected issues
- Commenting in the code with a link to the above doc with more context
- Revisiting at regular intervals to assess if work needs to be planned to address known risks
We believe the best plan is informed by high clarity on the problem space, which often means a certain amount of time doing hands-on “discovery”. We try to find a pragmatic balance between planning, building prototypes & team alignment before building the feature. This often depends on the size of the feature and who is the Subject Matter Expert (or teammates with prior experience). Ultimately our output should be tickets on our kanban board that are granular & specific enough to be achieved in a day. We optimize for the lowest amount of planning overhead – do what works for you and helps the team get aligned, but that allows you to get productive quickly. Team members should go out of their way to provide feedback and help avoid feedback that might be too late for the code review stage.
Each of us is an owner of our feature or fix from inception to deployment & monitoring. We each have a voice in the building of MasterHealth and are encouraged to come with ideas on user experience and implementation considerations. We’ll involve the necessary stakeholders during planning and execution, and verify what we ship is working as expected. Sometimes follow-up changes are necessary and as owners, we know when we’re needed to chip in or revisit our work.