It's easy to hear "Code is Law" and immediately think of blockchain, smart contracts, and the intricate digital agreements that govern decentralized systems. But what if I told you that the same principle, "Code is Law," can profoundly impact the everyday engineering practices within a software development team, making workflows smoother, more predictable, and ultimately, more human-friendly?
I remember my first day at a new role, eager to bring in some fresh ideas. My goal was to introduce a streamlined process for creating presentation slides, something that often becomes a bottleneck. Within two weeks, we had a basic, albeit unpolished, interface for it. Don't let the simple UI fool you; beneath that lies a sophisticated automation.
Imagine this: an engineer needs to create new slides. They simply create a new markdown file in a designated repository, following a set of clear standards. The magic happens next. A simple command, make create-pr, automatically updates the repository's version, and crucially, opens a pull request. Then, our trusty CI tool, Travis, kicks in. It builds the slides, ensuring everything is in order, and gives a green light. Once a reviewer approves and merges the pull request, Travis tags the version, creates a GitHub release, and deploys the slides to a static hosting service. The result? A versioned, accessible presentation, all initiated with minimal effort from the engineer.
This isn't just about making things look pretty; it's about removing friction. For the engineers, the user experience is clear and integrated into their daily work. Tedious tasks like manually updating version numbers or creating pull requests are reduced to a single command. Even for minor text edits, an inline editing option allows direct modification and PR creation without touching the command line.
So, why "Code is Law"? Because as I've worked more, I've realized the immense importance of automated and user-friendly processes. We all know security best practices, like key rotation, are vital, but how often do they actually get implemented? Often, it's because the process itself becomes too cumbersome, a tangled web of rules that defeats its own purpose. But when a process is baked into code, it's optimized, streamlined to the point where it's as simple as it can be without sacrificing completeness. Code becomes the immutable, reliable enforcer of these optimized workflows.
At Arcblock, this philosophy extends far beyond just slide creation. Our development process is built around "Code is Law" principles:
- Pre-commit Hooks: Every change must pass code style checks, static analysis, compilation, and unit tests before it can even be committed.
- Pull Requests: All changes require a pull request, must be up-to-date, pass CI checks, and have at least one review before merging.
- Merge Strategy: Every PR must be a squash merge, ensuring a clean commit history.
- Versioning and Releases: Each PR merge bumps the version, generates a changelog, and creates a GitHub release. Compiled artifacts or static website builds are automatically placed in the release or deployed to hosting.
Doing all this manually would be a nightmare for engineers. While clever engineers will automate parts of it, the real challenge arises when these automation mechanisms differ across repositories. This leads to a learning curve and context-switching costs. Our solution? A unified system where all repositories share a common structure and capabilities.
We approached this from a product design perspective, creating a command-line interface (CLI) tool, arcli, for engineers. It allows them to generate new repositories interactively, choosing templates like Elixir, Node.js, Python, or Web. This interactive approach ensures ease of use for newcomers, while also supporting JSON piping for programmatic integration, honoring the Unix philosophy.
A generated repository comes with a predefined layout and a suite of make commands ready to go: watch, dep, precommitall, deploy, release, browse-pr, doc, runbuild, init, bump-version, install, travisclean, lint, create-pr, post-build, travis-init, travis-deploy, delete-release, pre-build, and test. Imagine the power: you haven't written a line of your actual application code, but the entire world of development – from creating pull requests to managing dependencies – is already set up and functional.
Take make create-pr as an example. It automates the four steps an engineer would otherwise do: changing the version number, committing, writing a changelog, and creating the PR on GitHub. With one command, it handles all of that, streamlining the process significantly.
For projects requiring specific software installations, an init directory allows users to run make init for a fully configured development environment, eliminating lengthy installation guides. Similarly, deployment configurations for CI tools like Travis are managed within the repository structure.
The beauty of this system is "learn once, run everywhere." Once an engineer understands how one repository works, they understand them all. The complex development workflow is simplified to a few core commands: arcli init:repo, arcli start:story (which automatically creates a feature branch), git commit, and arcli create:pr.
The benefits of using code to enforce processes are numerous:
- Reuse: Implementations can be reused across different workflows.
- Compose: Processes can be combined and built upon each other.
- Compile: Workflows can be transformed into different structures.
- Configure: Processes are flexible, adjustable to different needs.
- Extend: Workflows can be inherited and expanded.
- Debug: Processes are easily debuggable.
- Test: Workflows can be thoroughly tested.
- Version: Processes have their own versioning.
- Document: Documentation lives alongside the code.
And, of course, the biggest advantage of code: rapid iteration. When an issue is found, it can be fixed and a pull request submitted almost instantly, inviting immediate feedback and collaboration. We recently refined our interview process, and within a day, PRs were being submitted based on discussions.
Think about the speed of this iteration. We often talk about avoiding rigid processes, encouraging participation and modification. But how do you modify a process that's a static poster on a wall or a PDF buried in an intranet? It involves feedback loops, unclear ownership, and a general sense of futility. Contrast that with a GitHub repository: with basic markdown skills, you can propose a change in minutes. Once merged, Travis handles the build and deployment, and your contribution is permanently recorded, much like a blockchain transaction.
Everyone champions the "lean startup" and the "build-measure-learn" feedback loop, recognizing that speed is paramount. But this speed isn't achieved through wishful thinking; it's built on robust mechanisms, and at its core, that mechanism is code. Even for something as seemingly simple as a code test for a data engineer, a suggestion can lead to a proposal for a new CLI command, like arcli create:code-test, demonstrating how quickly processes themselves can evolve when they are code-driven.
