← Back to Tutorials

Intermediate Guide: Key Concepts, Best Practices, and Next Steps

intermediatebest-practiceshow-toframeworkstips-and-tricks

Intermediate Guide: Key Concepts, Best Practices, and Next Steps

This tutorial is written for people who already know the basics of working in a terminal, using Git, writing code, and deploying something small. The goal is to connect intermediate-level concepts into a practical workflow you can apply to most software projects: how to structure work, keep systems reliable, and choose what to learn next.


What “Intermediate” Really Means

At an intermediate level, you’re usually past “how do I do X?” and into:

Intermediate work is less about clever code and more about repeatable systems.


1) Key Concepts You Need to Internalize

1.1 Determinism: Make Builds and Runs Repeatable

A deterministic workflow means:

Common sources of non-determinism:

Concrete practices:

Example (Node.js):

node --version
npm --version

npm ci
npm test

npm ci is designed for reproducible installs using the lockfile.

Example (Python with Poetry):

poetry --version
poetry install --sync
poetry run pytest -q

--sync aligns the environment exactly to the lockfile.


1.2 Boundaries: Define Interfaces Between Components

Intermediate systems break down when everything can call everything else.

Boundaries include:

Good boundaries have:

Practical example: validate API contracts

Example (generate OpenAPI docs with a typical toolchain):

# Example only; exact command depends on framework/tool
npm run openapi:generate

Even if you don’t generate code, writing a schema forces clarity.


1.3 Observability: Measure What Matters

Observability is not “logging a lot.” It’s being able to answer:

Three pillars:

  1. Logs (events)
  2. Metrics (numbers over time)
  3. Traces (request journeys across services)

At intermediate level, focus on:

RED method (services):

USE method (resources):

Example: inspect logs with jq

cat app.log | jq -r '.level + " " + .msg'

Example: follow container logs

docker logs -f myapp

1.4 Risk Management: Change Is the Biggest Source of Outages

Intermediate engineers treat changes as risky by default and mitigate with:

Key idea: reliability is a product of process, not heroics.


2) Best Practices That Scale

2.1 Git Workflows: Clean History, Safe Collaboration

You should be comfortable with:

Recommended baseline branch flow:

Useful commands:

git status
git switch -c feature/add-search
git add -A
git commit -m "Add search endpoint with pagination"
git fetch origin
git rebase origin/main
git push -u origin feature/add-search

Interactive rebase to clean up commits:

git rebase -i HEAD~5

Use this to squash “fix typo” commits before merging.

Tagging a release:

git tag -a v1.4.0 -m "Release v1.4.0"
git push origin v1.4.0

2.2 Project Structure: Make the “Happy Path” Obvious

A maintainable repository:

A practical, language-agnostic structure:

.
├── cmd/ or src/            # entrypoints
├── internal/ or app/       # core application logic
├── pkg/                    # reusable libraries (if applicable)
├── migrations/             # DB migrations
├── scripts/                # automation scripts
├── docs/                   # documentation
├── .github/workflows/      # CI pipelines
└── Makefile                # common commands

Makefile example (real commands):

.PHONY: test lint run

test:
	pytest -q

lint:
	ruff check .

run:
	python -m app

Then you can do:

make test
make lint
make run

2.3 Dependency Management: Pin, Audit, Update

Rules of thumb:

Node audit:

npm audit
npm audit fix

Python (pip-audit):

python -m pip install pip-audit
pip-audit

Update strategy:


2.4 Testing Strategy: Build a Pyramid That Reflects Reality

A common testing pyramid:

Unit tests should validate business logic without heavy I/O.

Integration tests should validate boundaries:

E2E tests should validate critical user journeys only.

Example commands (Python):

pytest -q
pytest -q -m integration
pytest -q --maxfail=1

Example commands (JavaScript):

npm test
npm run test:integration
npm run test:e2e

Coverage (Python):

python -m pip install coverage
coverage run -m pytest
coverage report -m

Coverage is a tool, not a goal. Optimize for meaningful assertions.


2.5 Code Reviews: Reduce Risk, Share Context

Good reviews focus on:

Reviewer checklist:

Author checklist:


3) Security Best Practices You Should Apply By Default

3.1 Secrets Management: Never Commit Secrets

Avoid storing secrets in:

Scan for accidental secrets (example with gitleaks):

gitleaks detect --source . --no-git

Rotate exposed secrets immediately. Deleting from Git is not enough.

Environment variables in local dev:

export DATABASE_URL="postgres://user:pass@localhost:5432/app"
export API_KEY="..."

Prefer a secrets manager in production (AWS Secrets Manager, GCP Secret Manager, Vault).


3.2 Principle of Least Privilege

Give services only what they need:

Example (Postgres):

-- Create a role with restricted permissions
CREATE ROLE app_user LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE app TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;

Then ensure migrations run with a separate, more privileged role.


3.3 Input Validation and Output Encoding

Assume all external input is hostile:

Validate at boundaries, fail fast, return safe errors.

Example (shell: validate required env var):

: "${DATABASE_URL:?DATABASE_URL is required}"

4) Performance and Reliability: Practical Techniques

4.1 Latency Budgets and Timeouts

Intermediate systems often fail due to missing timeouts.

Add:

Example (curl with timeout):

curl --max-time 3 https://example.com/health

Example (Postgres statement timeout):

SET statement_timeout = '2s';

4.2 Caching: Use It Deliberately

Caching improves performance but adds complexity:

Start with:

Example (inspect cache headers):

curl -I https://example.com/static/app.css

4.3 Backpressure and Rate Limiting

If your service can be overwhelmed, it will be.

Add:

Example (Nginx rate limiting snippet conceptually): You’d configure this in Nginx config, then reload:

sudo nginx -t
sudo systemctl reload nginx

5) Databases and Migrations: Avoid the Common Traps

5.1 Schema Changes Must Be Backward Compatible

A safe migration strategy:

  1. Add new column (nullable)
  2. Deploy code that writes both old and new
  3. Backfill
  4. Switch reads to new
  5. Remove old

Example migration workflow (generic):

# Run migrations
./scripts/migrate up

# Verify schema
./scripts/migrate status

Postgres: check locks and long queries

SELECT pid, state, query, now() - query_start AS duration
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY duration DESC;

5.2 Indexing: Measure Before and After

Indexes speed reads but slow writes and use disk.

Explain a query (Postgres):

EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC LIMIT 20;

Then add an index if justified:

CREATE INDEX CONCURRENTLY idx_orders_user_created_at
ON orders (user_id, created_at DESC);

CONCURRENTLY reduces lock impact but has tradeoffs.


6) Containers: Build, Run, Debug Like a Pro

6.1 Understand Images vs Containers

Core commands:

docker build -t myapp:dev .
docker run --rm -p 8080:8080 myapp:dev
docker ps
docker exec -it <container_id> sh

6.2 Use Multi-Stage Builds (Smaller, Safer Images)

A typical pattern:

Example commands to inspect size:

docker images | head
docker history myapp:dev

6.3 Debugging Containers

View logs:

docker logs -f <container_id>

Check environment:

docker exec -it <container_id> env | sort

Inspect ports:

docker port <container_id>

7) CI/CD: Automate the Boring and Risky Parts

7.1 What CI Must Do at Minimum

Typical CI commands:

npm ci
npm run lint
npm test

or:

poetry install --sync
poetry run ruff check .
poetry run pytest -q

7.2 CD and Release Hygiene

For deployments:

Example: embed Git SHA into build:

GIT_SHA="$(git rev-parse --short HEAD)"
echo "$GIT_SHA" > build/version.txt

8) Documentation That Actually Helps

Intermediate documentation is:

Docs you should have:

Example README quickstart section (commands):

git clone https://github.com/yourorg/yourrepo.git
cd yourrepo
cp .env.example .env
make run

9) Debugging and Incident Response: A Practical Workflow

9.1 Debugging Checklist (Production Mindset)

When something breaks:

  1. Define the symptom (what is failing, for whom)
  2. Check recent changes (deploys, config, migrations)
  3. Check dashboards (errors, latency, saturation)
  4. Inspect logs with correlation IDs
  5. Reproduce in staging or locally if possible
  6. Mitigate first (rollback, disable feature flag)
  7. Root cause analysis after stability

Find recent Git changes:

git log --oneline --decorate -n 20

Compare two versions:

git diff v1.3.0..v1.4.0

9.2 Runbooks and “Known Good” Actions

A runbook should include:

Example verification commands:

curl -fsS http://localhost:8080/health
curl -fsS http://localhost:8080/ready

10) Next Steps: How to Level Up Intentionally

Intermediate growth comes from choosing a direction and building depth. Pick one track for 4–8 weeks and ship something real.

Track A: Backend Reliability

Focus topics:

Load testing example (wrk):

wrk -t4 -c50 -d30s http://localhost:8080/api/items

Track B: Security Engineering

Focus topics:

Dependency scanning examples:

npm audit
pip-audit

Track C: Platform/DevOps

Focus topics:

Terraform basics (real commands):

terraform fmt -recursive
terraform validate
terraform plan
terraform apply

Track D: Frontend Systems

Focus topics:

Example (measure bundle size with a typical script):

npm run build
npm run analyze

A Practical “Intermediate” Checklist (Use This on Your Next Project)

Build & Run

Quality

Security

Reliability

Delivery


Closing Notes

The intermediate leap is mostly about turning knowledge into habits:

If you share what stack you’re using (Node/Python/Go/Java, Docker/K8s, Postgres/MySQL, cloud provider), I can tailor the commands, repo layout, and a concrete 2–4 week learning plan to match your environment.