# Code Reference — Comparisons

_9 pages_

---
# Comparisons (/docs/comparisons)



# Comparisons [#comparisons]

A vs B trade-offs.

Browse the sidebar for every cheat sheet in this section.

## Sections [#sections]

*All notes are listed in the sidebar.*


---

# Express vs Fastify (/docs/comparisons/express-vs-fastify)



# Express vs Fastify [#express-vs-fastify]

*Comparisons · Reference cheat sheet*

***

## 📋 Overview [#-overview]

**Express** is the ubiquitous minimalist Node framework. **Fastify** emphasizes performance, schema validation, and plugin encapsulation. Both are excellent — pick based on team needs and ecosystem.

## 🔧 Core concepts [#-core-concepts]

| Dimension      | Express              | Fastify                         |
| -------------- | -------------------- | ------------------------------- |
| Habit / hiring | Extremely common     | Growing, strong for APIs        |
| Validation     | DIY / middleware     | JSON Schema first-class         |
| Plugin model   | Middleware + routers | Encapsulated plugins            |
| Perf           | Good                 | Typically faster out of box     |
| TypeScript     | Manual/community     | Strong schema → types workflows |
| Ecosystem      | Largest              | Smaller but solid               |

**When to use Express:** existing Express apps, max middleware compatibility, simplest tutorials/hiring.

**When to use Fastify:** new JSON APIs wanting schemas, logging, and encapsulation by default.

## 💡 Examples [#-examples]

**Express:**

```js
import express from "express";
const app = express();
app.use(express.json());
app.post("/users", (req, res) => res.status(201).json(req.body));
app.listen(9000);
```

**Fastify:**

```js
import Fastify from "fastify";
const app = Fastify({ logger: true });
app.post(
  "/users",
  { schema: { body: { type: "object", required: ["email"], properties: { email: { type: "string" } } } } },
  async (req, reply) => {
    reply.code(201);
    return req.body;
  },
);
await app.listen({ port: 9000, host: "0.0.0.0" });
```

## ⚠️ Pitfalls [#️-pitfalls]

* Porting middleware 1:1 often fails — Fastify hooks ≠ Express middleware.
* Express async errors need wrappers (v4); don't assume parity with Fastify's async handling.
* “Faster” rarely matters until measured — schema safety often matters more.

## 🔗 Related [#-related]

* [Node/express\_basics.md](/docs/comparisons/../node/express-basics)
* [Node/fastify\_basics.md](/docs/comparisons/../node/fastify-basics)
* [README.md](/docs/comparisons)


---

# fetch vs axios (/docs/comparisons/fetch-vs-axios)



# fetch vs axios [#fetch-vs-axios]

*Comparisons · Reference cheat sheet*

***

## 📋 Overview [#-overview]

**fetch** is the web-standard HTTP client (also in Node 18+). **axios** is a popular library with richer ergonomics (interceptors, automatic JSON, better older-browser story historically).

## 🔧 Core concepts [#-core-concepts]

| Dimension       | fetch                                | axios                         |
| --------------- | ------------------------------------ | ----------------------------- |
| Availability    | Built-in                             | `npm install axios`           |
| JSON body       | Manual `res.json()`                  | Auto-parsed `response.data`   |
| HTTP errors     | Only network fail rejects; 404 is OK | Rejects on non-2xx by default |
| Interceptors    | DIY wrappers                         | First-class                   |
| Upload progress | Limited                              | Easier (`onUploadProgress`)   |
| Timeouts        | `AbortController`                    | `timeout` option              |

**When to use fetch:** browser-native, Next.js/edge, zero deps, Request/Response compatibility.

**When to use axios:** interceptors, uniform error handling, upload progress, large existing axios codebases.

## 💡 Examples [#-examples]

**fetch:**

```js
const res = await fetch("/api/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Ada" }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
```

**axios:**

```js
import axios from "axios";

const { data } = await axios.post("/api/users", { name: "Ada" });
```

## ⚠️ Pitfalls [#️-pitfalls]

* fetch does not throw on 404/500 — always check `res.ok`.
* axios throws — wrap in try/catch or check `validateStatus`.
* Credentials/cookies: fetch needs `credentials: 'include'`; axios uses `withCredentials`.

## 🔗 Related [#-related]

* [Javascript/fetch.md](/docs/comparisons/../javascript/fetch)
* [Node/getting\_started.md](/docs/comparisons/../node/getting-started)
* [README.md](/docs/comparisons)


---

# Pages Router vs App Router (/docs/comparisons/pages-vs-app-router)



# Pages Router vs App Router [#pages-router-vs-app-router]

*Comparisons · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Next.js **Pages Router** (`pages/`) is the original model. **App Router** (`app/`) is the modern default with Server Components, nested layouts, and richer caching controls. New projects should prefer App Router unless constrained.

## 🔧 Core concepts [#-core-concepts]

| Dimension          | Pages Router                            | App Router                              |
| ------------------ | --------------------------------------- | --------------------------------------- |
| Directory          | `pages/`                                | `app/`                                  |
| Default components | Client-centric pages                    | Server Components                       |
| Data fetching      | `getServerSideProps` / `getStaticProps` | async server components / `fetch` cache |
| API routes         | `pages/api/*`                           | `app/**/route.ts`                       |
| Layouts            | `_app` + manual                         | Nested `layout.tsx`                     |
| Metadata           | `next/head` / `Head`                    | `metadata` / `generateMetadata`         |

**When to use App Router:** new apps, RSC, nested layouts, modern Next features.

**When to use Pages Router:** large legacy codebases, libraries that assume Pages only, gradual migration.

## 💡 Examples [#-examples]

**Pages:**

```tsx
export async function getServerSideProps() {
  return { props: { t: Date.now() } };
}
export default function Page({ t }: { t: number }) {
  return <p>{t}</p>;
}
```

**App:**

```tsx
export default async function Page() {
  const t = Date.now();
  return <p>{t}</p>;
}
```

## ⚠️ Pitfalls [#️-pitfalls]

* Mixing both routers works but doubles mental models — document boundaries.
* Hooks require `'use client'` in App Router — easy to forget.
* Caching defaults in App Router surprise Pages veterans — learn `no-store` / revalidate.

## 🔗 Related [#-related]

* [Next.js/app\_router.md](/docs/comparisons/../next.js/app-router)
* [Next.js/pages\_router.md](/docs/comparisons/../next.js/pages-router)
* [Next.js/server\_components.md](/docs/comparisons/../next.js/server-components)
* [README.md](/docs/comparisons)


---

# pathlib vs os.path (/docs/comparisons/pathlib-vs-os)



# pathlib vs os.path [#pathlib-vs-ospath]

*Comparisons · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Choose **pathlib** for most new Python path logic (object-oriented, readable). Use **os.path** / `os` when maintaining legacy code or needing specific `os` side effects (cwd, env, process).

## 🔧 Core concepts [#-core-concepts]

| Dimension      | pathlib                    | os / os.path                 |
| -------------- | -------------------------- | ---------------------------- |
| Style          | Objects (`Path`)           | Strings                      |
| Join           | `/` operator, `joinpath`   | `os.path.join`               |
| Exists / mkdir | `path.exists()`, `mkdir()` | `os.path.exists`, `os.mkdir` |
| Glob           | `path.glob` / `rglob`      | `glob.glob`                  |
| Read/write     | `read_text` / `write_text` | open + join paths            |
| Best for       | New code, clarity          | Legacy, thin wrappers        |

**When to use pathlib:** new scripts, apps, anything that manipulates many paths.

**When to use os.path:** drop-in maintenance, or APIs that already require strings and `os.*` process calls.

## 💡 Examples [#-examples]

**pathlib:**

```python
from pathlib import Path

root = Path.cwd() / "data"
root.mkdir(parents=True, exist_ok=True)
for p in root.glob("*.csv"):
    print(p.read_text(encoding="utf-8")[:80])
```

**os.path:**

```python
import os

root = os.path.join(os.getcwd(), "data")
os.makedirs(root, exist_ok=True)
for name in os.listdir(root):
    if name.endswith(".csv"):
        path = os.path.join(root, name)
        with open(path, encoding="utf-8") as f:
            print(f.read(80))
```

## ⚠️ Pitfalls [#️-pitfalls]

* Mixing `Path` and strings casually works often (`os.fspath`) but be consistent at API boundaries.
* `Path` on Windows vs POSIX differs for anchors — test both if you ship cross-platform.
* `os.path` won't stop you from forgetting `exist_ok` / parents the way pathlib habits encourage.

## 🔗 Related [#-related]

* [Python/os.md](/docs/comparisons/../python/os) (if present)
* [Python/getting\_started.md](/docs/comparisons/../python/getting-started)
* [README.md](/docs/comparisons)


---

# pytest vs unittest (/docs/comparisons/pytest-vs-unittest)



# pytest vs unittest [#pytest-vs-unittest]

*Comparisons · Reference cheat sheet*

***

## 📋 Overview [#-overview]

**pytest** is the common default for modern Python tests (fixtures, parametrize, assert rewriting). **unittest** is the stdlib xUnit-style framework — fine for stdlib-only environments and legacy suites.

## 🔧 Core concepts [#-core-concepts]

| Dimension   | pytest                     | unittest             |
| ----------- | -------------------------- | -------------------- |
| Install     | Third-party                | Stdlib               |
| Assertions  | Plain `assert`             | `self.assertEqual`…  |
| Fixtures    | Elegant `@pytest.fixture`  | `setUp` / `tearDown` |
| Parametrize | `@pytest.mark.parametrize` | More boilerplate     |
| Plugins     | Rich ecosystem             | Limited              |
| Discovery   | `test_*.py` conventions    | `unittest` discovery |

**When to use pytest:** almost all application and library projects.

**When to use unittest:** zero-dependency constraints, teaching stdlib, or extending large existing unittest suites.

## 💡 Examples [#-examples]

**pytest:**

```python
import pytest

@pytest.fixture
def n():
    return 2

@pytest.mark.parametrize("x,y", [(1, 2), (2, 4)])
def test_double(x, y, n):
    assert x * n == y
```

**unittest:**

```python
import unittest

class DoubleTest(unittest.TestCase):
    def test_double(self):
        self.assertEqual(2 * 2, 4)

if __name__ == "__main__":
    unittest.main()
```

## ⚠️ Pitfalls [#️-pitfalls]

* Mixing styles in one repo without guidance confuses contributors — pick a default.
* pytest plugin overload can obscure failures — keep conftest lean.
* unittest mock (`unittest.mock`) works great *with* pytest too.

## 🔗 Related [#-related]

* [Pytest/](../Pytest/)
* [Python/getting\_started.md](/docs/comparisons/../python/getting-started)
* [README.md](/docs/comparisons)


---

# Redis vs Postgres (/docs/comparisons/redis-vs-postgres)



# Redis vs Postgres [#redis-vs-postgres]

*Comparisons · Reference cheat sheet*

***

## 📋 Overview [#-overview]

**Postgres** is a durable relational system of record. **Redis** is an in-memory data structure server optimized for speed, caching, ephemeral state, and specialized structures. Often used together, not as mutual substitutes.

## 🔧 Core concepts [#-core-concepts]

| Dimension         | Redis                      | Postgres                         |
| ----------------- | -------------------------- | -------------------------------- |
| Primary store?    | Usually no (cache/session) | Yes (SoR)                        |
| Durability        | Optional RDB/AOF           | WAL + strong durability defaults |
| Query model       | Keys + data structures     | SQL + relational model           |
| Latency           | Sub-ms in-memory           | Low ms; disk-backed              |
| Relations / joins | DIY                        | First-class                      |
| Memory cost       | Entire working set in RAM  | Buffer cache + disk              |

**When to use Redis:** cache, rate limits, sessions, leaderboards, pub/sub, queues (or Streams).

**When to use Postgres:** authoritative business data, complex queries, transactions, constraints.

## 💡 Examples [#-examples]

**Redis cache aside:**

```shellscript
GET user:42
# miss → load from Postgres → SET user:42 … EX 300
```

**Postgres source of truth:**

```sql
SELECT id, email FROM users WHERE id = 42;
```

## ⚠️ Pitfalls [#️-pitfalls]

* Using Redis as the only database without HA/persistence for critical data.
* Caching without invalidation strategy serves stale Postgres data forever.
* Storing huge blobs in Redis blows RAM — keep hot, small keys.

## 🔗 Related [#-related]

* [Redis/getting\_started.md](/docs/comparisons/../redis/getting-started)
* [Postgres/getting\_started.md](/docs/comparisons/../postgres/getting-started)
* [Redis/expiry\_ttl.md](/docs/comparisons/../redis/expiry-ttl)
* [README.md](/docs/comparisons)


---

# Tailwind vs hand-written CSS (/docs/comparisons/tailwind-vs-css)



# Tailwind vs hand-written CSS [#tailwind-vs-hand-written-css]

*Comparisons · Reference cheat sheet*

***

## 📋 Overview [#-overview]

**Tailwind** accelerates UI via utility classes and a shared design scale. **Hand-written CSS** (or CSS Modules / plain stylesheets) offers full control and fewer markup classes. Many teams mix: Tailwind for app UI, custom CSS for complex animations or third-party overrides.

## 🔧 Core concepts [#-core-concepts]

| Dimension          | Tailwind                | Hand-written CSS            |
| ------------------ | ----------------------- | --------------------------- |
| Speed of iteration | Very fast for common UI | Slower unless well-factored |
| Bundle discipline  | Scans used classes      | Depends on your imports     |
| Readability        | Busy class strings      | Centralized selectors       |
| Design consistency | Theme tokens by default | You must enforce tokens     |
| Learning curve     | Utility vocabulary      | CSS cascade/specificity     |
| Dynamic theming    | Variants + CSS vars     | Full freedom                |

**When to use Tailwind:** product UIs, design systems with utility tokens, rapid component work.

**When to use CSS:** intricate animations, unique marketing layouts, existing CSS architecture, or teams that dislike utilities-in-markup.

## 💡 Examples [#-examples]

**Tailwind:**

```html
<button class="rounded-md bg-zinc-900 px-3 py-2 text-sm text-white hover:bg-zinc-700">
  Save
</button>
```

**CSS:**

```css
.button {
  border-radius: 0.375rem;
  background: #18181b;
  padding: 0.5rem 0.75rem;
  font-size: 0.875rem;
  color: white;
}
.button:hover {
  background: #3f3f46;
}
```

## ⚠️ Pitfalls [#️-pitfalls]

* Tailwind without content paths generates empty CSS.
* Hand-written CSS without conventions recreates a one-off design system by accident.
* `@apply` everywhere reintroduces the problems utilities were meant to solve.

## 🔗 Related [#-related]

* [Tailwind/getting\_started.md](/docs/comparisons/../tailwind/getting-started)
* [CSS/](../CSS/)
* [README.md](/docs/comparisons)


---

# useEffect vs useLayoutEffect (/docs/comparisons/useeffect-vs-uselayouteffect)



# useEffect vs useLayoutEffect [#useeffect-vs-uselayouteffect]

*Comparisons · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Both React hooks run after render from side-effect logic. **useLayoutEffect** fires synchronously after DOM mutations before paint; **useEffect** fires after paint. Prefer `useEffect` unless you must measure/mutate DOM before the user sees a frame.

## 🔧 Core concepts [#-core-concepts]

| Dimension      | useEffect                     | useLayoutEffect                            |
| -------------- | ----------------------------- | ------------------------------------------ |
| Timing         | After paint                   | Before paint (sync)                        |
| Blocking       | Non-blocking paint            | Can delay paint                            |
| SSR            | OK (warns if misused)         | Warning on server — often gate             |
| Typical use    | Fetch, subscriptions, logging | DOM measure, scroll lock, tooltip position |
| Default choice | Yes                           | Only when needed                           |

**When to use useEffect:** data loading, event listeners, integrating non-React libs that can wait a frame.

**When to use useLayoutEffect:** avoid flicker when reading layout (`getBoundingClientRect`) and synchronously updating state/DOM.

## 💡 Examples [#-examples]

**useEffect (fetch):**

```tsx
useEffect(() => {
  let cancelled = false;
  fetch("/api").then(async (r) => {
    const data = await r.json();
    if (!cancelled) setData(data);
  });
  return () => {
    cancelled = true;
  };
}, []);
```

**useLayoutEffect (measure):**

```tsx
useLayoutEffect(() => {
  const rect = ref.current?.getBoundingClientRect();
  if (rect) setWidth(rect.width);
}, [deps]);
```

## ⚠️ Pitfalls [#️-pitfalls]

* Putting heavy work in `useLayoutEffect` janks the UI.
* `useLayoutEffect` on the server warns — use `useEffect` or client-only components.
* Neither replaces data libraries (react-query) for complex fetching.

## 🔗 Related [#-related]

* [React/](../React/)
* [Next.js/server\_components.md](/docs/comparisons/../next.js/server-components)
* [README.md](/docs/comparisons)

