How Clinch Talent is built
Ship working valuable software frequently
This article describes what it is like to work with the Clinch Talent engineering team. We are a small, high performance team that produces recruitment marketing software and this is our take on how to build software.
What we do:
Ship small and frequently
The Clinch Talent team pushes to production 40-50 times a week. Our staging and production builds are rarely more than 20 minutes apart. How do we achieve this with a relatively small team and codebase that started in 2014?
If you work here, you might take on a big feature that will take two weeks to complete. The feature requires new database columns, UI code, background jobs, and third party integrations. Perhaps in your old job you were used to coding for two weeks and submitting a large code change towards the end.
In this team, you would discuss the feature with another team member, create a rough plan, and push some code to production within a couple of hours of starting. For example, you might choose to submit a pull request (PR) containing a feature flagged menu option plus a test. Next you might submit a new route, controller and some HTML with a test that calls the route and expects the HTML back. Then the database migration would get submitted.
Within a few hours, three PRs have been quickly reviewed, tested, and have made their way to production. At the code changes trickle in to production, UX can be invited to give feedback at a very early stage. By the time it gets to Friday afternoon, there is visible progress to show stakeholders on the weekly demo. And by the following week, some customers will have the feature flag enabled so they can get early access.
Contrast this with the popular approach of the developer disappearing for days or weeks at a time to work on a huge chunk of the feature and submitting a code bomb. The review process is tiresome. Test cases get missed. Integration is more difficult.
We ship small and frequently. It is a simple idea but it is not easy to do. Every new team member likes the idea of it during the interview process, but in the beginning struggles to do it. It takes practice and discipline to split the code into small chunks that don’t invalidate the system.
To get a PR approved quickly:
- Either change the structure the code, or add new behaviour, but don’t do both. See Kent Beck’s talk or article on structure vs behaviour.
- If it’s new behaviour, test it.
- Don’t make the codebase worse with unreadable code, unnecessary code or duplication.
- Keep it small. How small is too small? A PR will never be rejected for being too small unless it is going to cause an error or lower test coverage.
Once your code has been merged to staging, it’s upto you to see it through to production and sanity check it. Does this many times a day, every day and you won’t go wrong.
Test relentlessly
We run over 9000 unit tests and several hundred capybara tests with every code change. Builds that have less than 100% line coverage of Ruby code will fail. In the Clinch Talent team, humans write tests and computers run them.
We use Minitest which is a simple test framework, as well as being the Rails default.
New team members quickly pick up the the basics of test fixtures, common setup and assertions. Because Clinch Talent is built with server side generated HTML,
we are able to use Minitest to check the results of controller actions, using assert_select
.
Occasionally we have to use Javascript on a screen and to test that we use Capybara. Capybara tests are slower to write, debug, and run. They are more resource intensive, and are much more prone to random failure. With our current effort to replace handwritten Javascript with Hotwire, developers spend more time reliably asserting Turbo Streams and Turbo Frames with regular Minitest and less time writing Capybara tests.
As a developer you have to get used to writing and crafting tests. They should not be mindlessly copied and pasted, but care should be taken not to refactor them into complexity. As a reviewer of code submissions you have to get used to looking for scenarios that have not been tested e.g.
- Has the new
create
controller action in the PR been tested forsave
success and failures path - Are there assertions statements, checking that the values of the newly created ActiveRecord are correct and proving that Strong Parameter validation is in place?
- If a Turbo Stream was returned in response, has the HTML of the Turbo Stream been asserted?
- What if an authorised user tries to access this particular action?
Simplify constantly
The goal is to have the least amount of code complexity in the system, while delivering the most value possible. The less code complexity that exists in the codebase, the less there is for a developer to read. The simpler the system is, the faster code can be changed and tested, and the faster we can ship to production and respond to customer needs.
The tools we choose help us to simplify constantly. We use Ruby on Rails and try to stay as close to the default Rails way as much as possible. Testing is done with Minitest and Capybara. Data is accessed with ActiveRecord and MySQL. Views are generated with HTML, Turbo and Javascript. Assets are built with sprockets and esbuild. Third party libraries are updated multiple times per week, and if possible eliminated in favour of a built-in Rails feature. We do our best to stay on the latest versions of the entire stack.
Ruby and Rails definitely give us a competitive advantage, but particularly because of how we use it. Plenty of teams might pick Rails and dig themselves into a hole by forking Rails, never upgrading, avoiding automated testing, building single page apps, and managing multiple microservices.
There is also great attention to detail given to individual lines of code during the review process. This is a crucial part of simplifying constantly. The code has to pass the tests and not reduce code coverage. It must be understandable. Duplication has to be minimised or eliminated, and extra code should be deleted.
Hit dates
A Clinch Talent feature will never be reported as being 6 months, or even a year late - a common trend in our industry. Because of our discipline in Shipping small and frequently, Testing relentlessly, and Simplifying constantly, we are able to deliver partial features very early on. Our stakeholders (UX, support, accounts managers, customers) steer the development of the feature, getting weekly updates at our engineering demo.
We’re also experts at delivering just enough software. When deciding how much is enough, we weigh up who will use the feature, how often it will get used. If it’s something critical like our page editor, then it gets UX attention, feedback sessions and beta releases. If it’s a setting that will be configured once by our professional services team, then there will be zero frills.
What we don’t do:
Specialists
There are no dedicated front-end developers, back-end developers, DBAs, testers, infrastructure experts, Scrum masters, Agile coaches or DevOps people on the team.
The expectation is that everyone on this team is fluent in all of the technologies necessary to ship a feature by themselves - HTML, Javascript, CSS, Ruby, Rails, MySQL and AWS.
To date, most Ruby people who join us have little to no front end experience and have been writing JSON APIs, tested with RSpec. With some hand-holding, a new team member will pick up enough skills in HTML, ERb, Bootstrap, and Minitest to ship something within the first week. In fact, if you join us, you’ll push something to production on the first day.
Single Page Applications (SPAs)
It takes more code and effort to build a rich-client javascript front-end with a Rails back-end serving JSON, than it does to build a server side Rails application.
Browsers are also a lot quicker at rendering HTML directly, than receiving JSON and converting it to DOM elements.
Since 2014 we have been generating HTML on a server, passing it to a browser, and sprinkling it with javascript. In the last 2 years we have adopted Turbo and Stimulus to get all of the benefits of dynamic/partial page reloads without the pain of the Javascript ecosystem.
Less than 2% of our codebase is Javascript and developers might go weeks without touching it.
Microservices
The Clinch Talent codebase is best described as a “modular monolith” in that there is one code repository, but there is enforcement of subsystem boundaries.
Subsystem boundaries are enforced with Packwerk. If you accidentally cross a boundary or create a circular dependency between two subsystems, your build will fail. Having the code split into subsystems via folders, while keeping one repository and one deployment has several advantages:
- The enormous overhead of managing microservices is avoided.
- It stops the codebase turning into a big ball of mud
- A developer can work within one subsystem and forget about the rest of the codebase.
- Faster builds: CI can be tuned to detect what subsystems need to be tested
“Agile”
Scrum planning, retrospectives, refinement sessions, pre-refinement sessions, story points, velocity, and burn down charts. We don’t do any of this.
The philosophy since day one of Clinch Talent has been “as little process as possible, but no less”. We have minimised ceremonies to a daily 10-15 update meeting at 10:00am, a demo session every Friday at 3pm where you showcase your work and a two weekly meeting with your manager. If you like to code, you’ll have plenty of time to do it.
Conclusion
We tend to attract people who are open to what we do, and have been burnt by what we don’t do. Our latest hire talked of being in three hour pre-refinement session meetings in the previous job. We’d rather you spend that time creating something valuable for a customer.
The upside of our style is working is, you can spend the vast majority of your time building things.
The downside is that under-performers can’t hide. You can’t fake the number of approved PRs you got merged this week, and you can’t fake your way through a weekly demo for very long. If you’re used to bigging up the small of contributions, avoiding work, or secretly holding down a second job, this isn’t for you.