Engineering

Communication

Bramble Engineering values clear, concise, transparent, asynchronous, and frequent communication. Here are our most important modes of communication:

Keeping yourself informed

As part of a fully-distributed organization such as Bramble, it is important to stay informed about engineering-led initiatives.

We employ multimodal communication, which describes the minimum set of communication channels we’ll broadcast to.

For the Engineering department, any important initiative will be announced in:

  • The Engineering mailing list
    • All members of the department should become members as part of the onboarding process. If this is not the case for you, reach out to your manager.
  • Slack
    • #status-updates
      • The channel membership is mandatory
    • #team-engineering
    • #triage-beta
    • #team-security
    • #quality
    • #ux

If you frequently check any of these channels, you can consider yourself informed. It is up to the person sharing to ensure that the same message is shared across all channels. Ideally, this message should be a one sentence summary with a link to an issue to allow for a single source of truth for any feedback.

Prioritizing technical decisions

Please see the Product Management section that governs how they prioritize work, and also should guide our technical decision making.

Despite the high priority of velocity to our project and our company, there is one set of things we must prioritize over it: Bramble availability & security. Neither we, nor our customers, can run an Enterprise-grade service if we are willing to risk users' productivity and data.

Our Engineers collectively make thousands of independent decisions each day that can impact brmbl.io and our users and customers there. They all need to keep availability and security in mind as we endeavor to be the most productive engineering organization in the world. We can only move as fast as brmbl.io is available and secured.

For security, we prioritize it more highly by having strict SLAs around priorities labels with security issues. This shows a security first mindset as these issues take precedence in a given timeframe.

The Importance of Velocity

  • The rate at which Bramble delivers new value to users in the form of features is a competitive advantage for the project and the company.
  • In order to ensure that the product vision remains intact, the bulk of our energy is directed toward one version of Bramble.
  • Companies tend to slow down as they grow. It takes deliberate effort to prevent this, so it must always be top of mind.
  • Once you slow down, it is incredibly painful to speed back up again.

Incremental Velocity and Measurement

Our velocity should be incremental in nature. It’s derived from our MVC-based approach, which encourages “delivering the smallest possible solution that offers value to our users”. This could be a small new feature, but also includes code improvements, bug fixes, etc.

To measure this, we count and define the target here: Development Department Narrow MR Rate which is a goal for managers and not ICs.

For example, an MR rate of 11 translates to roughly one MR every 1½ business days with time for overhead. To attain this, Product Development Engineers are encouraged to:

  • Fix small problems they see in the codebase without an issue resulting in incremental improvements. Small here translates to ½ day or less.
  • Fix small bugs (particularly bug::transient type bugs). Small here translates to 1/2 day or less.
  • For feature issues, break the issue into several smaller MRs that are delivered incrementally. Small here translates to less than two days.
  • Fix an issue identified within the codebase. E.g. fix a code climate issue.
  • Raise concerns for issues where our incremental philosophy does not work and the issue cannot be broken down further.
  • Raise concerns for issues that Product Development Engineers do not feel fit the MVC definition.

Velocity over predictability

We optimize for shipping a high volume of user/customer value with each release. We do want to ship multiple major features in every monthly release of Bramble. However, we do not strive for predictability over velocity. As such, we eschew heavyweight processes like detailed story point estimation by the whole team in favor of lightweight measurements of throughput like the number of merge requests that were included or rough estimates by single team members.

There is variance in how much time an issue will take versus what you estimated. This variance causes unpredictability. If you want close to 100% predictability you have to take two measures:

  1. Invest more time in estimation to reduce that variance. The time spent estimating things could otherwise be used to create features.
  2. Leave a reserve of time with unscheduled work so you can accommodate the variance. According to Parkinson’s law, the work expands so as to fill the time available for its completion. This means that we’re not adhering to our iteration value and that for the next cycle our estimates for comparable features will be larger.

Both measures reduce the overall velocity of shipping features. The way to prevent this is to accept that we don’t want perfect predictability.

Note: This does not mean we place zero value on predictability. We just optimize for velocity first.

Balance refactoring and velocity

When changing an outdated part of our code (e.g. views, JS modules), use discretion on whether to refactor or not. For long term maintainability, we are very interested in migrating old code to the consistent and preferred approach (e.g. Tailwind, LiveView), but we’re also interested in continuously shipping features that our users will love.

Aim to implement new modules or features with the preferred approach, but changing preexisting non-conforming parts is a gray area.

If the weight of refactoring and other constraints (such as time) risk threatening the availability of a feature, then strongly consider refactoring at another time. On the other hand, if the code in question has hurt availability or poses a threat to it, then strongly consider prioritizing refactoring. This is a balancing act and if you’re not sure where your change should go (or whether you should do some refactoring before hand), reach out to another Engineer.

If it makes sense to refactor before implementing a new feature or a change, then please:

  • Create separate merge requests for the refactoring and change. This aids maintainability and code review.
  • Notify your engineering manager and relevant stakeholders (preferably in an issue comment) of the relevant scope increase and rationale.

If it is decided not to refactor at this moment, then please:

  • Make sure a descriptive “technical debt” issue exists for this refactoring.
  • Notify your engineering manager so that the refactoring issue can be weighted and scheduled.

Iteration

We always push ourselves to be iterative and make the minimal viable change. The image below provides an example of how we should iterate:

build a car vs start with skateboard and move to car

Image Credit: Henrik Kniberg from Crisp

When iterating, our goal is to build something quick and functional. In the example above, we should aim to build something that can transport us from A to B even though it may not have all of the nice-to-have features like an engine, seats, or air conditioning.

The example’s upper sequence shows how we should not iterate. The problem with going from a wheel to a wheel base to a frame to a car is that the first few iterations are not functional.

The example’s lower sequence shows how we should iterate. Building a skateboard is low complexity and can be assembled in a day while building a car is high complexity and takes thousands of parts and a much longer assembly time. A skateboard is not as fast as a car yet it can still transport a person. Each subsequent iteration provides the person with more speed, more control, and a better aesthetic.

One common misconception of iteration is that there is no waste. Using the example above, the parts of a skateboard can be reused in a scooter, however, they likely cannot be reused in a car. Iteration often requires us to throw away product or code to make way for a better product.

To read more about iteration, see our values page and engineering workflow.

Collaboration

To maintain our rapid cadence of shipping, we must keep the barrier low to getting things done. Since our team is distributed around the world and therefore working at different times, we need to work in parallel and asynchronously as much as possible.

That also means that if you are implementing a new feature, you should feel empowered to work on the entire stack if it is most efficient for you to do so.

Nevertheless, there are features whose implementation requires knowledge that is outside the expertise of the developer or even the group. For these situations, we’ll require the help of an expert in the feature’s domain.

In order to figure out how to articulate this help, it is necessary to evaluate first the amount of work the feature will require from the expert.

If the feature only requires the expert’s help at an early stage, for example designing and architecting the future solution, the approach will be slightly different. In this case, we would require the help of at least two experts in order to get a consensual agreement about the solution. Besides, they should be informed about the development status before the solution is completed. This way, any discrepancy or architectural issue related to the current solution, will be brought up early.

Code Quality and Standards

We need to maintain code quality and standards. It’s very important that you are familiar with the [Development Guides] in general, and the ones that relates to your group in particular:

  • UX Guides - WIP
  • Backend Guides - WIP
  • Frontend Guides - WIP
  • Database Guides - WIP

Please remember that the only way to make code flexible is to make it as simple as possible.

Quality is everyone’s responsibility

It is important to remember that quality is everyone’s responsibility. Everything you merge to master should be production ready. Familiarize yourself with the [definition of done].

Release when it’s ready

Deadline pressure logically leads to a few outcomes:

  1. People are at increased risk of burnout.
  2. We may compromise on our definition of done.
  3. We cut scope.
  4. We miss the deadline.

Only the last two outcomes are acceptable as a general rule. Missing a ‘deadline’ in the form of an assigned milestone is often OK as we put velocity above predictability.

For these reasons, and others, we intentionally do not define a specific date for code to be merged in order to reach a self-managed monthly release. The earlier it is merged, the better. This also means that:

  1. We don’t want merge request authors to work extra hours or otherwise rush to meet a deadline.
  2. We don’t want reviewers and maintainers to be put under pressure to do anything other than meet the regular SLOs.

If it is essential that a merge request make it in a particular release, this must be communicated well in advance to the engineer and any reviewers, to ensure they’re able to make that commitment. If a severe bug needs to be fixed with short notice, it is better to revert the change that introduced it than to rush, or even to delay the release until the fix is ready.

In general, there is no need to change any behavior close to release dates.