- 11 minute read

Speed vs. Safety: Building developer experience in a MedTech startup

When I first started thinking about how to build a great developer experience while maintaining rigorous compliance standards, I admit to seeing them as competing priorities: speed vs. safety, or, to put it another way, developer autonomy vs. audit bureaucracy.

I was wrong. What I’ve learned is that great developer experience and strong compliance can genuinely reinforce one another.

As CTO I lead technology and security at Macuject, a MedTech startup developing cutting-edge solutions to end preventable blindness. MedTech comes with numerous compliance requirements, and for us, part of that means ensuring that annual SOC 2 and HIPAA audits are successful.

The key is treating compliance not as a set of hurdles to clear, but as a design challenge, and it works. At Macuject, we consistently achieve annual attestations for SOC2 and HIPAA while our developers ship features quickly and enjoy their work. The secret? Making the compliant path the easy path.

Developers tolerate friction they understand: Explain the ‘why’

Developers tolerate friction they understand. They resent friction that seems arbitrary.

Macuject invests significant time explaining why our compliance requirements exist. In MedTech, we can point to real breaches, real consequences, real harm to patients. When developers understand that the controls they’re working within exist to protect vulnerable people, the whole thing feels different. It’s not bureaucracy; it’s responsibility.

Explaining the why needs to be done well and often. A session during onboarding is essential; continuing to talk about the benefits and reasons on an ongoing basis is invaluable for keeping the understanding front of mind.

Gates as confirmations: Build quality in first

In compliance-heavy environments, there’s a need to add gates that review, scan, and approve work. Gates provide a point of reference when auditors ask how the company ensures security, quality, and reduces risk during product development, providing a detailed history of who did what and when.

However, gates that slow developers down create pressure to circumvent them. Slow feedback loops mean developers have moved on to other work by the time they learn about issues, making fixes more expensive, time-consuming, and frustrating. Remove these issues for developers by ensuring checkers, linters, analysers, and tests run locally in the IDE and before remote git branch pushes, not just in CI. A local verification process that catches an issue immediately beats a pipeline failure every time. The developer is still in context, still thinking about that code, and can fix the problem as part of their normal flow. Of course, local checks can technically be bypassed, but in practice, developers want fast feedback; they use the local tools because they make their lives easier.

We still have gates; these can’t be bypassed. CI runs checkers, linters, analysers, and tests. We require human review of all changes. We need formal, documented User Acceptance Testing (UAT) sign-off by clinical experts for all functional changes. Finally, there is leadership approval for release. However, by the time the code reaches those gates, most issues have already been caught and fixed. The gates become confirmations, not blockers, and work continues to flow.

Tip: Eliminate low-value decision-making. Find an automatic code style validator, such as Rubocop, adopt it, and mandate it for all work. There is no greater waste of expensive engineering time than arguing about syntax during a code review.

The duality: Impatience to automate, patience for rigour

In a previous post, I explored the leadership necessity of leading with impatience and the patience to see it through. Our approach to compliance is the practical application of this duality.

Compliance is the patience. It is the understanding that quality, security, and safety are non-negotiable and take time. It protects patients, doctors, our business, and ultimately our ability to keep doing the work we love.

Developer experience is the impatience. It is the refusal to accept that “rigour” must equal “slowness,” and the drive to automate friction out of existence.

When you combine these, you get our strategy: Use the impatience of engineering to automate the patience of compliance.

Compliance as code: Automate the audit trail relentlessly

If developers have to remember to do something for compliance, they’ll resent it. The resentment isn’t about the compliance requirement itself; it’s about the cognitive load and the interruption to flow.

The solution: Automate relentlessly.

All of our developers adhere to a common approach for two key aspects of their work:

Branch naming: You likely use something similar <JIRA-KEY>/descriptive-name-here, e.g. `WA-1023/add-photos-to-user-profiles`.

PR templates: We use a standard PR template across all repositories with headings and checklists related to our business, SOC2, and HIPAA. A PR template like ours takes a slight adjustment for newcomers and adds a couple of minutes to PR creation time. Still, once you get into the flow of it, there aren’t any objections; it helps developers think through the PR and ensure it meets all needs.

With these two primitives in place, we can now automate many tasks. Let me give you some concrete examples of what this looks like from a web application perspective; similar processes exist for our data and platform developers as well.

Pull requests

We leverage GitHub automations and Jira API connections here when a new PR is opened by a developer.

  1. The PR is automatically linked to Jira based on branch naming, no big deal; this is standard practice.
  2. PR names are automatically pre-pended with the Jira key. “Adds user profile photos” is automatically modified to “WA-1023: Adds user profile photos”.
  3. A comment gets added to the PR that provides a convenient link back to the Jira issue.
  4. Our extensive template is pre-populated, and developers fill in several details, including:
    • The level of risk the change poses to the business, e.g., changes that affect authentication or PHI handling, is automatically flagged as high risk and receives extra scrutiny during review.
    • Changes needing extra scrutiny during UAT.
    • Asks from other teams to make the change successful, e.g., ensuring a firewall change is already in place.
  5. The Jira ticket is automatically moved from “In Development” to “In Review” to align with our Kanban process.
  6. Checkers, Linters, Analysers and Tests are then invoked, in order from quickest to complete to longest. We always want to exit as quickly as possible when CI detects a problem.
  7. Once passed, human review is requested, either automatically or dynamically, depending on the type of change.
  8. Once approved and merged:
    • The entire PR thread gets copied to Jira and added as a comment on the original ticket, including any uploaded media such as images. Copying PR descriptive content ensures that those involved in UAT, who may not have access to GitHub, can read the entire background if helpful for their work.
    • Unless otherwise tagged, the PR gets assigned to the next release defined in Jira.
    • The Jira ticket is automatically moved from “In Review” to “Ready for UAT.” These statuses maintain the necessary separation of duties: developers write and review code. At the same time, clinical experts perform UAT to verify that features function correctly within medical workflows and meet regulatory requirements. Developers cannot sign off on their own UAT. Only after clinical sign-off and final release approval is a ticket marked as “Done.”

Releases

When we’re ready to release the web application, a lengthy process ensues that relies on semantic versioning (semver) tagging and git release branches.

Previously, it took half a day to assemble all the pieces we needed to demonstrate compliance manually. Once proven stable and repeatable across multiple releases, we introduced automation here as well.

It now takes 30 minutes to complete the same process for our release owner using a release script and a combination of GitHub and Jira automations.

  1. Every issue assigned to the release has its PR content parsed by the release script:
    • Identifying how many changes fall into the low, medium, and high categories.
    • Collating all details for changes that need to be considered by the UAT team as additional information for their work in validating the release.
    • Likewise, for other teams, collate all the changes required by this release so our release owner can communicate them for scheduling purposes.
  2. An overall release category is calculated based on the identified low, medium, and high risk categories from the associated PRs.
  3. Using a Jira release template, the release script documents the release, including all the details above.
  4. The release owner creates customer-facing notes that include product images, videos, and other helpful content to explain the new features and changes. Once fully documented in Confluence, the new page gets amalgamated into the release documentation.
  5. One or more release candidates are published to our UAT environment, allowing UAT and medical teams to assess, request modifications and finally approve the release.
  6. Finally, the leadership team must formally approve the release. We can then proceed with the release process:
    • Final binaries are built and published to our environments.
    • The release gets posted to Slack as a celebration. Some channels receive lower-level technical details, others a higher-level summary, and the customer-facing change notes. Our CEO, COO and other leadership get these as well, which happily automates a small part of my job.
    • I’m not covering how releases appear in development, UAT, and production environments in both Australia and the United States. This post is already too long; needless to say, this is also automated, with each environment reporting its deployment status back into Jira.

The pattern here is consistent: identify the compliance-driven tasks that create friction, then automate them so thoroughly that developers hardly notice they’re happening. The controls exist, the audit trail is available, but the cognitive burden disappears.

Infrastructure as code: Eliminate drift, enable compliance

Environmental issues are a classic source of frustration. Code works in development but fails in production. Configuration drift across environments creates mysterious bugs. Managing multiple regions globally while expanding semi-regularly is the stuff of nightmares. When the CFO comes knocking about cloud costs, you end up in a cold sweat.

In a compliance context, this is even more problematic; you need to demonstrate that your production environment has the controls you’ve documented, is secure, considers PII and PHI fully, enforces PoLP, is backed up, can be restored from a clean slate in a complete disaster scenario and can report on every single change made.

Our solution has been to invest heavily in Infrastructure as Code (IaC). We’ve built an extensive AWS CDK project in TypeScript that defines all of our infrastructure. This single codebase sustains Development, UAT, and our Production environments in both Australia and the US. Adding another region becomes an easily quantifiable piece of work, measured in days.

The choice of TypeScript for our CDK project has been particularly valuable. Compile-time type checking catches misconfigurations before deployment (well, usually, it is AWS, ask me about war stories). You find out about problems when you run the build, not when CloudFormation fails halfway through a deployment.

AWS itself provides best practices for the CDK and compliance validation with tooling called CDK Nag. CDK Nag allows CI to fail builds unless developers have done the right things or documented a valid exception state, something which our PR reviewers must validate and agree to.

A change to the infrastructure follows the same path as the web application discussed earlier; the only difference is that, instead of a binary, the final output is AWS infrastructure being created, updated, or removed.

Beyond developer workflow: The rest of the compliance picture

This post focuses on developer workflow automation and on how we’ve made compliance controls feel natural rather than burdensome in day-to-day development. But this is just one piece of a comprehensive compliance program. What I haven’t covered here, but Macuject does rigorously, includes:

  • Security monitoring and incident response: Centralised logging, real-time alerting for security events, defined escalation procedures, and regular tabletop exercises.
  • Vendor management: Due diligence reviews, BAA management, annual vendor security assessments.
  • Data governance: Data retention policies, PHI data flow mapping, data deletion procedures.
  • Security training: Regular security awareness and HIPAA training, random phishing simulations.
  • Penetration testing and vulnerability management: Annual pentests, continuous vulnerability scanning, and a defined SLA for patching critical issues.
  • Physical and administrative safeguards: Device management, clean desk policies, and background checks.

Make no mistake, obtaining annual attestations for SOC2, HIPAA, and others is no easy task and requires the involvement of the entire business to be successful.

Final thoughts: Start with the friction!

If you’re working in a similar environment and feeling the tension between compliance and developer productivity, start by asking your developers what slows them down most. I’d bet that you can solve at least some of these without compromising compliance posture. Often, the solution will strengthen both.

Let’s move the conversation from “How do we pass the audit?” to “How do we design our developer experience to make compliance a feature, not a hassle?”

I hope you find the content here helpful. Please reach out if you have any questions.