👋 We’re Knock. We provide a set of simple APIs developers use to introduce notifications into their products, without needing to build and maintain a notification system in-house.
These days more engineering teams are electing to use 3rd-party vendors for non-core (but critical) pieces of their stack, instead of building in-house.
But as teams bring vendors into parts of their stack that once lived in in-house code, they run into a new set of challenges.
- Managing vendor-controlled configuration when it lives outside owned repos
- Testing changes to the vendor model and automating that with CI
- Coordinating deployment across owned code and vendor code
- Rich, dynamic typing for the vendor data model
With the last generation of developer tools, great docs, SDKs, and example apps were the bar for a great developer experience. Now these are table stakes.
The best modern developer tools are pushing to bring their APIs and SDKs into the developer workflow of their customers to let them manage 3rd-party application-level infrastructure as though they’d built it in-house themselves.
In this post we’ll look at five principles modern developer tools use to meet developers where they work and the benefits of that approach.
The five principles of modern developer experience (or, bring the tooling to the developer)
Here are the principles of the modern developer tool that emerged from our own work solving customer challenges at Knock, and that we're seeing in other developer tools we use.
Work with resources in code
The first principle of modern developer platforms (and the one from which all of its other principles emerge) is the ability to express and work with resources from the vendor model in code.
This means that you can take any third-party vendor resource that you’d normally define in the vendor’s dashboard or service, and can work with it in your IDE.
Checkly’s monitoring as code workflow is a good example of this. Any test you’d create or update in the Checkly dashboard can be created or updated locally. This means you can run tests locally, ensure the test executes as expected, and then push it back up to Checkly to run automated monitoring.
Note that this is a two-way sync. The Checkly CLI includes Constructs, which are the “as code” representations of the resource (e.g. an API test) that’s created/updated/deleted when you deploy your local test configuration to Checkly.
A big benefit of this approach: you don’t lose the benefits that come with a vendor dashboard that you (or non-developers such as product managers and support teams) can use to manage and debug the system.
This also leads to another big benefit: source control management.
Source control management
When a developer tool provides an “as code” representation of its resources (as Checkly does with its Constructs concept), you get the added benefit of managing and versioning those resources the same way you would any other code: with your source control management tool of choice.
This means that now the vendor’s resources are part of your normal code review and version control, and can be diffed and rolled back with ease.
The best experiences we’ve seen here are ones in which the vendor lets you use your own source control management workflow and repo as the source of truth for the vendor dashboard itself. This means that the vendor’s dashboard just points at whatever branch in your repo you tell it to, and you own the source of truth.
Take Retool source control. In Retool, every component and logical rule you build into your internal tools in the Retool dashboard can be worked with as code. This makes version control, diffs, and rollbacks easy. Since it’s all in code you can just manage it in your normal SCM workflow.
Another example of this principle in action is Tinybird’s Versions feature. With Versions, Tinybird supports managing your data projects in code, with full support for branching via environments. This allows you to bring your data pipelines and projects into your SCM, and lets you easily iterate on your data pipelines without affecting code running in production. The added benefit here is all of your code is version-controlled and reviewed as part of your standard software development lifecycle.
Rich type definitions
The next principle of modern developer platforms is that they can include rich type definitions. While most modern SDKs will export type definitions for typed languages, best-in-class developer tools are going a step further to export customer-specific types from dynamic configuration.
Here’s a great example of this in action from Rudderstack, where their Ruddertyper tool takes an event from your Rudderstack library and uses it to generate a type definition you’ll use when introducing your analytics calls into your codebase.
Another cool example of this comes from the feature flag platform Hypertune, where type-safe feature flags improve the experience of both adding feature flags in your code, highlighting potential feature flag errors as you introduce them, and finding all instances of a flag across your codebase.
Adding these rich-type definitions has a couple benefits:
- Types help with discovery, showing available methods and the configuration required
- Types can be used to catch errors at compile-time either in a CI run or surfaced in the editor for missing or incorrect properties
Run tests locally and in your CI workflow
As you’re working with a vendor’s “as code” resources locally, a natural next step is to see if they’re working as expected. Modern developer tools include
test commands in their CLIs so you can test your latest work without leaving the context of your IDE or terminal.
The Checkly CLI includes the
npx checkly test command to run all of your checks on the Checkly cloud infrastructure. At Knock, our CLI provides a
knock workflow run command you can use to test a specific notification workflow and see the output.
Because these tests are CLI commands, you can also include them as automated tests in your CI pipeline. This ensures that before you ship anything to production (whether it’s directly related to the vendor in question or not) you can run an automated test to ensure you haven’t broken anything.
Manage as part of your deployment lifecycle
Once you’ve tested your code and everything looks good, it’s time to deploy to production. When you’re using 3rd-party tools for systems that sit on the critical path of your application (such as authentication or notifications), you want to coordinate the deployment of your service and your third-party vendor to avoid any potential changes that could cause downtime.
The modern developer tool anticipates this problem, providing CLI commands or actions for coordinating automated deploys to their service that can be integrated into a CI/CD pipeline.
As an example, at Knock our CLI includes
promote command that customers can use to promote their changes to an environment in our system. With
knock commit promote --to=production, a customer can confirm that any Knock changes in their PR will be pushed to Knock when they merge to main.
Checkly provides a similar command—
npx checkly deploy—which publishes all checks to your Checkly cloud. Airplane also includes a deployment and promotion system via their
airplane deploy --env prod command, allowing you to easily coordinate the deployment of your application and your internal tools.
Tinybird goes a step further in their Versions feature, automatically bootstrapping a Github actions CD script as part of the project initialization that can be used to version and release changes to your data pipelines. They also expose this behavior as a
tb deploy command in their CLI that the CD script uses.
Benefits of developer tooling that works where you do
When an application-infrastructure-as-a-service vendor provides tooling for bringing their service into your workflow, there are a number of benefits that emerge—aside from the obvious benefit of not having to build and maintain that service in-house.
- Tools that adapt to your workflow, and not vice versa. A big benefit of these tools working locally and in CI/CD pipelines is that developer teams don’t need to change their workflow to incorporate a vendor. In many ways, working with these tools feels like working with a service they built in-house. You spend less time navigating into a 3rd-party dashboard and more time working on your core product.
- Vendor “as code” concepts and types live alongside application code. Typing means that type errors and other issues are caught in development, and not when a team is getting ready to deploy (or already has 😬). Vendor code living in your repo also means it’s run through your normal code review process, and that you have a versioned, diffed history of all changes you’ve made to the vendor’s configuration.
- Vendor changes can be made safely and in sync with your application. There will always be a coupling between your system and the third-party service, so having ways to safely change, test, and deploy those changes is essential to guarantee the operation of your service.
We’re seeing an industry shift take place as more engineering and product teams look to use application-infrastructure-as-a-service platforms to offload the management of non-core (but critical) services.
These teams can move faster, deliver a better end-user experience, and scale for the long-term, while still building a product to the specs they want. As more of these tools adopt the principles outlined above, we’ll see accelerated adoption of these platforms as developers gain the ability to manage 3rd-party services as though they were their own.
If you’re a Knock customer today (or have been following along on our changelog), you probably recognize a lot of this from the Knock CLI and management API. There’s also a lot you probably don’t recognize—we have a lot of work to do! You can expect to see more from us on this front as we continue to build the developer-first customer engagement platform.