COPY . . before installing dependencies — invalidates the dependency-install layer's cache on every single code change, turning a cached, near-instant step into a full reinstall on every build.RUN than the one that created it — the bytes are already committed to an earlier layer and still ship in the image; clean up within the same RUN, or use a multi-stage build.CMD/ENTRYPOINT for a process that needs to shut down gracefully — the shell becomes PID 1 and can silently swallow the signal docker stop sends.depends_on alone (without a healthcheck + condition: service_healthy) to wait for a dependency like a database to actually be ready, not just started.ENV/COPY and assuming a later "delete" step removes it from history.ubuntu instead of alpine/slim/distroless) or a missing .dockerignore sending the whole repo (including .git and node_modules) into the build context.FROM node:latest means your "identical everywhere" promise silently breaks the moment upstream publishes a new latest.Containers are usually one piece of a larger deployment story — see CI/CD Pipelines Explained for how an image actually gets built, tested, and promoted to production, and Microservices vs. Monolith for how container boundaries tend to map onto service boundaries once a system has more than one of them. This project's own infra/ folder has a real Docker Compose setup for a self-hosted code-execution engine, if you want to see one in context. Practice the concepts here as runnable exercises in the code lab.