# How to run developer management routines as a fractional CTO

> Set up a weekly update, one-to-ones, an architecture review, a decision log, and an escalation rule that run without you, then check them after a week away.

Source: https://voxgig.com/howto/run-developer-management-as-a-fractional-cto

- Audience: platform-team
- Level: advanced
- Verified: 2026-10-15
- Published: 2026-10-15

## Short answer

Run four routines that work without you in the room: a weekly update with a named author, fortnightly one-to-ones, a monthly architecture review, and a decision log that names each decider. Write an escalation rule that lists what may interrupt an off day, and keep yourself off the incident rota. Then take a week away and check that the update went out, the log grew, and each incident went to its owner.

---
## You will need

An engineering team of three to twelve people, and a tracker the whole team already uses, so the
work is visible without a meeting. Node 22 or later for the checker, which uses no packages. The
team, its decisions, and its incidents in the sample are fixtures.

## Approaches compared

The routines need somewhere to make work visible. Three trackers, compared on that job alone:

| Approach | When it fits | What it costs you | When to pick something else |
| --- | --- | --- | --- |
| [GitHub Projects](https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects) | The code and its issues already live on GitHub, and one team works in one project | Status updates are written by hand per project, so the routine depends on someone remembering | Several teams need one roadmap, or the work is not code |
| [Jira](https://support.atlassian.com/jira-software-cloud/docs/what-is-a-jira-software-project/) | The client already runs Jira, or several teams share one workflow | Configuration is a job of its own, and a two-day CTO cannot also be its administrator | A team under twelve with no Jira to inherit |
| [Linear](https://linear.app/docs/initiative-and-project-updates) | A product team that wants cycles, triage, and project updates with scheduled reminders | A seat per person, and a tool the client pays for after you leave | The client will not add a tool, or the team already lives in GitHub |

Pick the one the team already opens every day. Project updates in Linear carry a health indicator.
A workspace can also set reminders that ask each project lead for one at a regular cadence, which
suits a CTO who is not there to ask. GitHub Projects keeps the work beside the code, but its status
updates rely on someone remembering.[^1] Jira earns its place only when it is already the client's system of record.

## Choose the engagement shape

The shape decides how much control you have and what each day of it is for. More days buys more
control, and spreads the same judgment thinner across more hours.

| Shape | Days | Decides | Fits when |
| --- | --- | --- | --- |
| Technical advisor | A few hours a month | Nothing; advises the founder or CTO who decides | A technical leader exists and wants a second opinion |
| Retained fractional CTO | One or two days a week, open-ended | Architecture and hiring, through the routines on this page | No full-time CTO yet, and a team of three to twelve |
| Interim CTO | Four or five days a week, for a fixed term | Everything a CTO decides, until a permanent one starts | The previous CTO left, or a crisis needs daily presence |

A fractional engagement fails as an interim one done on two days. Every decision then waits two
days for you, and the team learns to wait. The routines below are what make two days enough.

## Set the four routines

Each routine has an owner who is not you, a cadence, and an artifact a stranger could read.

The weekly update is written by the tech lead and sent by 17:00 UTC on Friday. It names what shipped,
what was decided, with decision ids, and who owns incidents next week. Fortnightly one-to-ones run on
your in-office days and never move to an off day, which keeps those days free.

The monthly architecture review reads the decision log rather than starting from slides. Any
entry can be reopened there, which is what lets you delegate the decision in the first place. The
log follows the
[architecture decision record](https://www.cognitect.com/blog/2011/11/15/documenting-architecture-decisions)
format at a smaller size: a numbered entry, a date, a decider, and a status.[^2]

## Write the escalation rule

Write down what may interrupt an off day, and who decides everything else. The sample keeps it in
`team.json`, where the checker can read it:

```json title="team/team.json"
  "cto": "Sam Rivera",
  "updateDue": { "weekday": "Friday", "timeUtc": "17:00" },
  "escalation": {
    "interruptCtoFor": ["sev1", "security"],
    "everythingElse": "the incident owner on the rota decides; the tech lead decides architecture inside the guardrails"
  },
```

Two things interrupt: a sev1, and anything tagged security. A sev2 at 03:00 belongs to the
incident owner, who can wake a teammate but not you.

## Hand incidents to a named owner

The trap is becoming the on-call senior engineer. You fixed the hardest incident in your first
month, so the next one comes to you, and your management days become repair days. Keep yourself off
the rota. Each week names one incident owner, and that person runs the incident as its
[incident commander](https://response.pagerduty.com/training/incident_commander/), delegating the
hands-on work rather than doing it.[^3]

```js title="routines.mjs"
  const owner = team.incidentOwners[week]
  const allowed = new Set(team.escalation.interruptCtoFor)
  if (owner === cto) results.push({ held: false, routine: 'incident owner', detail: `the rota names ${cto} for ${week}; take yourself off it` })
  // An incident whose time cannot be read belongs to no week, and would otherwise vanish from all of them.
  for (const inc of incidents.filter((i) => Number.isNaN(instant(i.opened)))) {
    results.push({ held: false, routine: 'incident owner', detail: `${inc.id}: opened "${inc.opened}" needs a date and time with a zone` })
  }
  for (const inc of incidents.filter((i) => inWeek(i.opened))) {
    if (!owner) results.push({ held: false, routine: 'incident owner', detail: `${inc.id}: nobody on the rota for ${week}` })
    else if (inc.handledBy !== owner) results.push({ held: false, routine: 'incident owner', detail: `${inc.id} ${inc.severity} handled by ${inc.handledBy}, not ${owner}` })
    else results.push({ held: true, routine: 'incident owner', detail: `${inc.id} ${inc.severity} handled by ${owner}` })
```

The checker fails a rota that names you, even in a week with no incidents, because the habit forms
before the incident does. It also fails an incident whose `opened` time has no zone, because
`Date.parse` would read that time in the zone of whichever machine runs the check. The same rule
applies to the update's `Sent` line.

## Take a week away and check

The test of the routines is an unplanned week without you. Afterwards, run the checker on that ISO
week and on an earlier one for comparison:

```bash
node routines.mjs 2026-W41 2026-W38
```

```text output
2026-W41 (2026-10-05 to 2026-10-11), Sam Rivera away
  held          weekly update  sent by Priya Shah at 2026-10-09T15:40:00Z
  held          decision log   2 entries: D-032 by Priya Shah, D-033 by Tom Okafor
  held          incident owner INC-111 sev2 handled by Tom Okafor
  held          incident owner INC-112 sev1 handled by Tom Okafor
  held          escalation     INC-112 sev1 interrupted Sam Rivera, which the rule allows
  all 5 checks held
2026-W38 (2026-09-14 to 2026-09-20), Sam Rivera away
  DID NOT HOLD  weekly update  written by Sam Rivera, sent 2026-09-21T09:30:00Z
  DID NOT HOLD  decision log   no entries dated in 2026-W38; D-030 (2026-09-21) decided by Sam Rivera on return
  DID NOT HOLD  incident owner INC-108 sev2 handled by Sam Rivera, not Ana Costa
  DID NOT HOLD  escalation     INC-108 sev2 interrupted Sam Rivera, which the rule does not allow
```

Week 38 is the before picture. The update was written on the Monday after, by the CTO. The one
decision raised that week waited for the return. A sev2 went to the person who had fixed the last
one instead of the person on the rota. Week 41 is the same team after the routines. The tech lead wrote the
update, two people made decisions, and the one interruption was a sev1, which the rule allows.

## Check it worked

Run the tests. Thirteen cases cover ISO week boundaries, both fixture weeks, a late update, a missing
`Sent` line, a decision with no decider, an empty rota, a security tag, and a rota that names you.
Three of them cover a time with no zone, last week's update copied with its `Sent` line, and an
incident the checker cannot place in a week.

```bash
node --test
```

```text output
1..13
# tests 13
# suites 0
# pass 13
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 111.739191
```

The literal to look for in your own week away is `all 5 checks held`, or however many checks the
week produced. One `DID NOT HOLD` line names the routine to fix first.

## When it goes wrong

The decision log fails in a quiet week. Nothing needed deciding, so nothing was logged. Read the
hint: if no decision you made in the three days after your return could have been made while you
were gone, the week passed.

The update goes out late, every week, by an hour. The deadline sits on a day the tech lead spends
in a customer meeting. Move the deadline, not the author.

The team escalates a sev2 because the rule is ambiguous. "Customer-facing" and "severe" mean
different things to each person. Name the severity levels and tags, as `interruptCtoFor` does, and review
each escalation at the next architecture review.

## When not to do this

Do not take a fractional role on a team with nobody who can write the update or own an incident.
The routines delegate to people, so a team with no tech lead leaves them nobody to delegate to. That
team needs an interim CTO or a first senior hire.

Do not use the decision log to approve decisions after the fact. An entry that waits for your
signature is a queue, and the checker counts it as one.

Do not install a tracker for the routines' sake. If the team lives in GitHub, GitHub Projects is
enough, and a second tool splits the work you are trying to see.

Do not test the routines with a planned week away. A planned absence gets prepared for, so it tests
a team that had warning rather than the one you will have on an ordinary Tuesday.

## Related how-tos

- [Audit a DevRel program in one week](/howto/audit-a-devrel-programme-in-one-week)

- [Run a changelog channel developers read in time](/howto/run-a-changelog-channel-developers-read)

## Last verified

Verified 2026-10-15 against Node 22.22.0. Every output block is what the command preceding it
printed. The team, its decision log, its updates, and its incidents are fixtures written for this
page; the tracker and incident behavior is quoted from each vendor's documentation as read on that
date.

[^1]: A new status update in
[GitHub Projects](https://docs.github.com/en/issues/planning-and-tracking-with-projects/sharing-project-updates)
starts from the last one. The form defaults to the previous update's status, start date, and target
date, so an update nobody edits repeats last week's. It is sometimes accurate.

[^2]: Michael Nygard proposed the format in a 2011 post titled
[Documenting Architecture Decisions](https://www.cognitect.com/blog/2011/11/15/documenting-architecture-decisions),
with sections for context, decision, status, and consequences. Near the end the post points out
that it is formatted like an ADR itself. Its status section reads, in full, "Accepted."

[^3]: PagerDuty's
[guide for incident commanders](https://response.pagerduty.com/training/incident_commander/) says
the commander becomes the highest ranking individual on a major incident call, regardless of their
day-to-day rank. It asks for someone willing to remove distractions from the call, even if it's the
CEO. A CTO who joins the call can be asked to leave it by the person on the rota.