Problem
Purple Network built Kiwici for teams and agencies that ship sites and apps without a DevOps engineer. A user connects a Git repository, sets up environments with variables, domains and databases, and runs build and deploy. Builds run on Jenkins and environments run on Kubernetes, but the user never touches either of them.
The dashboard covers the whole flow. It has projects and environments, build pipelines with live logs, rerun and cancel, databases and SFTP users, Git provider connections, team invites with roles, billing, and an AI assistant that streams its answers.
Architecture
The dashboard is a React single-page app. It talks only to the Kiwici API, built with Symfony and API Platform, using a Cognito token. The API stores data in MySQL, starts builds on Jenkins, manages environments on Kubernetes and bills usage on Stripe. Git access goes through a separate Laravel service, the Git gateway. It handles OAuth with GitHub, GitLab and Bitbucket and returns repositories, branches and clone credentials.
Scope
Mine
- The dashboard, almost all of it (1,106 of 1,257 commits), and the landing page
- The frontend architecture and the tests that enforce its rules
- The end-to-end stack and suite
- Part of the Git gateway: repository listing for the three providers, the GitHub App installation, grants per connection
- API changes for the Git flow, environment creation and user visibility across shared workspaces
Other teams
- Most of the API, the data model and billing (Purple Network backend team)
- Build and runtime infrastructure: Jenkins and Kubernetes
Key decisions
Enforce the frontend rules with tests
Why. The codebase has several authors and grew over three years. The rules are simple: the view renders, a controller hook decides, a service talks to the API, and every external input is parsed with Zod. Architecture tests check them. Each test counts violations per file and compares them with a list of known debt. A new violation fails. A fixed one also fails until the list is lowered, so the list can only shrink. Automated reviews by AI agents check the same rules before each commit.
Trade-off. The checks are regex scans, so code review still has to catch the edge cases. The known-debt lists are visible debt that someone has to pay down.
Hermetic end-to-end tests
Why. Signup, invites, payment and Git connections cross many services. The end-to-end stack runs the real frontend, the real API and MySQL with all migrations. Only third parties are simulated: Cognito, Stripe, Jenkins and the Git gateway. The tests run offline, without keys, and give the same result every time.
Trade-off. Some things can only be checked on the real services, such as 3-D Secure and email delivery. The first start runs 90 migrations and takes 5 to 7 minutes.
One gateway for three Git providers
Why. GitHub, GitLab and Bitbucket each have their own OAuth flow, tokens and API. The gateway hides the differences behind one REST interface, and the dashboard only talks to the Kiwici API. Adding a provider or changing a token flow doesn’t touch the frontend.
Trade-off. It is one more service to deploy and monitor, and its errors have to reach the user with a clear reason. The API passes the gateway’s error through to the dashboard.
In code
An architecture test that can only get stricter, simplified:
import known from "./zod-usage.known.json";
describe("Zod usage — ratchets", () => {
CHECKS.forEach((check) => {
it(check.name, () => {
const actual = check.counts(sources); // violations per file
const allowed = known[check.name] ?? {};
const report: string[] = [];
for (const file of new Set([...Object.keys(actual), ...Object.keys(allowed)])) {
const n = actual[file] ?? 0;
const limit = allowed[file] ?? 0;
if (n > limit) report.push(`${file}: ${n}, ${limit} allowed — ${check.rule}`);
if (n < limit) report.push(`${file}: ${n}, list says ${limit} — lower it`);
}
expect(report.join("\n")).toBe("");
});
});
});Results
- 157
- tagged releases of the dashboard
- ~2,500
- unit tests, including the architecture checks
- 32
- end-to-end tests on a hermetic stack
The dashboard has shipped 157 tagged releases. It has about 2,500 unit tests and 32 end-to-end tests. I can’t share usage numbers.