First Light
A medium SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- medium
- Seniority
- L6
- Asked in
- Samsara
Problem
A repo's early days last until its first successful CI build, and every repo reached that point at its own moment. Using repo_commits and the build history in ci_builds, report each author's average lines added and average lines removed per commit over the commits from those early days, biggest contributions first.
Every repo has a scrappy stretch before its first green build. See who was shipping then, and how big their changes were.
Tables
repo_commits
Columns: commit_id INTEGER, repo_name TEXT, author TEXT, message TEXT, added INTEGER, removed INTEGER, commit_at TEXT
| commit_id | repo_name | author | message | added | removed | commit_at |
|---|---|---|---|---|---|---|
| 3061 | backend-api | bob | Add unit tests | 17 | 7 | 2026-02-02 01:09:00 |
| 3122 | infra | charlie | Refactor auth module | 34 | 14 | 2026-03-03 02:18:00 |
| 3183 | ml-pipeline | charlie | Update deps | 51 | 21 | 2026-04-04 03:27:00 |
| 3244 | mobile-app | dana | Improve perf | 68 | 28 | 2026-05-05 04:36:00 |
| 3305 | data-platform | frank | Add caching layer | 85 | 35 | 2026-06-06 05:45:00 |
| 3366 | frontend | Alice | Fix typo in readme | 102 | 42 | 2026-07-07 06:54:00 |
| 3427 | backend-api | Bob | NULL | 119 | 49 | 2026-08-08 07:03:00 |
| 3488 | infra | alice | WIP | 136 | 56 | 2026-09-09 08:12:00 |
| 3549 | ml-pipeline | bob | 153 | 63 | 2026-01-10 09:21:00 | |
| 3610 | mobile-app | charlie | Add search endpoint | 170 | 70 | 2026-02-11 10:30:00 |
ci_builds
Columns: build_id INTEGER, repo_name TEXT, branch TEXT, status TEXT, dur_secs INTEGER, trigger TEXT, built_at TEXT
| build_id | repo_name | branch | status | dur_secs | trigger | built_at |
|---|---|---|---|---|---|---|
| 5047 | backend-api | develop | failed | 34 | pull_request | 2026-02-02 01:13:00 |
| 5094 | infra | feature/auth | canceled | 53 | schedule | 2026-03-03 02:26:00 |
| 5141 | ml-pipeline | fix/login-bug | running | 72 | manual | 2026-04-04 03:39:00 |
| 5188 | mobile-app | release/v2.0 | queued | 91 | tag | 2026-05-05 04:52:00 |
| 5235 | data-platform | hotfix/payment | success | 110 | webhook | 2026-06-06 05:05:00 |
| 5282 | frontend | feature /spaces | failed | NULL | push | 2026-07-07 06:18:00 |
| 5329 | backend-api | main | canceled | 148 | pull_request | 2026-08-08 07:31:00 |
| 5376 | infra | develop | running | 167 | schedule | 2026-09-09 08:44:00 |
| 5423 | ml-pipeline | feature/auth | queued | 186 | manual | 2026-01-10 09:57:00 |
| 5470 | mobile-app | fix/login-bug | success | 205 | tag | 2026-02-11 10:10:00 |
Expected output
| author | avg_lines_added | avg_lines_removed |
|---|---|---|
| alice | 256.8 | 92.8 |
| eve | 252 | 110.18181818181819 |
| charlie | 250 | 90 |
| Alice | 250 | 90 |
| frank | 244.55555555555554 | 85.66666666666667 |
Worked solution and explanation
What this problem really is
Strip the costume and this is a per-repo cutoff. Every repo has its own boundary, the moment of its first status = 'success' build, and each commit has to be judged against its own repo's boundary before anything is averaged. Anyone can write the MIN. What separates candidates is refusing to collapse it into one number. A scalar (SELECT MIN(built_at) FROM ci_builds WHERE status = 'success') returns the earliest green build anywhere, which on this seed belongs to 'frontend', and every repo that went green later loses the commits it made in between. 'infra' reached its first green build about six weeks after 'frontend', and an early 'infra' commit from that stretch silently drops out of its author's averages.
Step 1: Find each repo's first green build
Build first_success from ci_builds: filter to status = 'success', group by repo_name, and take MIN(built_at) as first_success_at. The filter sits inside the CTE so the MIN only ever sees green builds. The result is one row per repo, six on this seed.
Step 2: Hand every commit its own repo's cutoff
Join repo_commits to first_success on repo_name, then keep rc.commit_at < fs.first_success_at. The join key is what makes the cutoff move from repo to repo. The comparison is strict, so a commit at the exact instant of the green build is not early. Both timestamps are ISO text, so string order is time order.
Step 3: Average per author, biggest first
Group the surviving commits by rc.author, take AVG(rc.added) and AVG(rc.removed), and sort by avg_lines_added descending. Authors are grouped as stored, so 'Alice' and 'alice' come out as two rows.
The solution
WITH first_success AS (
SELECT repo_name, MIN(built_at) AS first_success_at
FROM ci_builds
WHERE status = 'success'
GROUP BY repo_name
)
SELECT
rc.author,
AVG(rc.added) AS avg_lines_added,
AVG(rc.removed) AS avg_lines_removed
FROM repo_commits rc
JOIN first_success fs ON rc.repo_name = fs.repo_name
WHERE rc.commit_at < fs.first_success_at
GROUP BY rc.author
ORDER BY avg_lines_added DESC- One global cutoff
WHERE rc.commit_at < (SELECT MIN(built_at) FROM ci_builds WHERE status = 'success')measures every commit against the first green build of 'frontend', the earliest on the seed. Early commits to 'infra' and 'ml-pipeline' that came after it vanish.- Each repo's own cutoff
- Joining on
repo_namegives each commit its own repo'sfirst_success_at. A commit counts exactly when its repo had not yet gone green, which is what early days means.
Common follow-up questions
- How would you include authors on repos that never logged a successful build? (Tests switching to an outer join and deciding what cutoff a never-green repo gets.)
- What if early meant between a repo's first and second successful builds? (Tests picking the second green build per repo without hardcoding dates.)
- An author committed early to two repos. Should their averages blend both windows? (Tests noticing the grain is the author, not the author per repo.)
- What changes if 'Alice' and 'alice' are confirmed to be the same person? (Tests normalizing the grouping key and what that does to the averages.)
- Which indexes on `repo_commits` and `ci_builds` let the planner skip full scans? (Tests indexing for the status-filtered aggregate and for the join plus range filter.)