Developer Academy

Code is not slower prose. It is a different keyboard.

Source code has roughly five times the symbol density of English, almost no natural word rhythm, and capitalisation that changes meaning. Typing Coach measures code speed, symbol accuracy, bracket accuracy and capitalisation separately, because improving one does not improve the others.

Symbols first, if you are honest about it

Most developers who feel slow at typing code are not slow at the letters - they are slow at brackets, operators and the shifted number row, which are handled almost entirely by the little fingers. The Symbol Academy trains exactly those.

Symbol Academy
! @ # $ %^ & * ( )[ ] { }< >, . ; : ' "_ - + =/ ? \ |` ~

All snippets

Array pipelineJavaScript
const active = users
  .filter((user) => user.status === "active")
  .map((user) => ({ id: user.id, name: user.name }))
  .sort((a, b) => a.name.localeCompare(b.name));
()=>.{}
Async request with error handlingJavaScript
async function loadProfile(id) {
  const res = await fetch(`/api/profile/${id}`);
  if (!res.ok) {
    throw new Error(`Request failed: ${res.status}`);
  }
  return res.json();
}
{}()`$
Reduce to a lookupJavaScript
const byId = records.reduce((acc, record) => {
  acc[record.id] = record;
  return acc;
}, {});
[]{}=>,
Interface and genericTypeScript
interface Result<T> {
  data: T | null;
  error?: string;
  meta: { page: number; total: number };
}

function unwrap<T>(result: Result<T>): T {
  if (result.data === null) throw new Error(result.error ?? "empty");
  return result.data;
}
<>:;|?
Nested query objectTypeScript
const users = await db.user.findMany({
  where: {
    active: true,
    createdAt: { gte: startOfMonth },
  },
  select: { id: true, email: true, sessions: { take: 5 } },
  orderBy: { createdAt: "desc" },
});
{}:,()
Comprehension and slicingPython
scores = [row["wpm"] for row in sessions if row["accuracy"] > 95]
top_ten = sorted(scores, reverse=True)[:10]
average = sum(top_ten) / len(top_ten)
[]:_
Class with a dataclassPython
@dataclass
class Session:
    wpm: float
    accuracy: float
    duration_ms: int

    def net_words(self) -> float:
        return self.wpm * (self.duration_ms / 60_000)
:_()=
Context managerPython
with open("results.csv", newline="") as handle:
    reader = csv.DictReader(handle)
    for row in reader:
        if int(row["errors"]) == 0:
            clean.append(row)
():",
Join with aggregationSQL
SELECT u.id, u.email, COUNT(s.id) AS sessions, AVG(s.net_wpm) AS avg_wpm
FROM users u
JOIN typing_sessions s ON s.user_id = u.id
WHERE s.created_at >= DATE('now', '-30 days')
GROUP BY u.id, u.email
HAVING COUNT(s.id) > 5
ORDER BY avg_wpm DESC
LIMIT 20;
(),.=
UpsertSQL
INSERT INTO key_performance (profile_id, key_char, attempts, correct)
VALUES (?, ?, ?, ?)
ON CONFLICT(profile_id, key_char) DO UPDATE SET
  attempts = key_performance.attempts + excluded.attempts,
  correct = key_performance.correct + excluded.correct;
(),=_
Accessible form fieldHTML
<div class="field">
  <label for="email">Email address</label>
  <input id="email" name="email" type="email" required />
  <p class="hint" id="email-hint">We never share your address.</p>
</div>
<>="/
Grid layoutCSS
.dashboard {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.5rem;
  padding: clamp(1rem, 4vw, 3rem);
}
{}:;-(
Configuration objectJSON
{
  "name": "typing-coach",
  "version": "1.0.0",
  "engines": { "node": ">=20" },
  "keywords": ["typing", "training", "touch-typing"],
  "private": true
}
{}":,[
README sectionMarkdown
## Getting started

1. Install dependencies with `npm install`.
2. Copy `.env.example` to `.env.local`.
3. Run `npm run dev` and open [localhost:3210](http://localhost:3210).

> Note: the database is created automatically on first run.
#-`[](