Programming can be called the art of making immensely big problems smaller and solving them one by one. Programmers divide and conquer entire systems that span across thousands of lines of code, making it all work like a giant clock day by day going full-on with an extremely low chance of an error happening, due to a sophisticated composition of its pieces and thorough testing achieved by experimenting and embracing chaos.

Sometimes, it is hard to pinpoint a small issue that’s going on. Tens of libraries, three frameworks and two synced-up databases your project employs to live up after a decade of development by several teams make it so hard to distinguish a small faulty click in an endless rattling of all the components of your program. There are a bunch of middlewares intercepting your request, then another bunch intercepting the response, some global configurations, sprinkled with a new library you never heard of and some additional services that need to be managed to start and work in the rhythm of the rest of your system.

Lego train in a forest city

To solve that, just like every open-source maintainer or a random fellow on StackOverflow will ask you, we can challenge our bugs to a duel we can use a little box where we extract only the thing we need to inspect. Those are called sandboxes, playgrounds, testbeds, reproductions. Just like Storybook allows us to enter the museum of our precious buttons and inspect each and one of them in every possible configuration, a small folder with just a beginner Vite+React template can help us identify why the library is not working as intended, or a composition of different docker-compose configurations can make it seamless to start the service from scratch, without all the overhead a dozen of tables incur when we are just trying to test out this simple stored procedure.

We can make sure that we’re allowed to do whatever the heck we want, be it sudo rm -rf / --no-preserve-root or whatever else. Everything is contained in a box and will never get out, like package.json’s dependencies. We will not just forget to undo this little change that we added to test our existing code, or remove the package that we don’t need, we have no chance of nuking our production database. Since we have this freedom, we are able to experiment beyond what is written in the docs, which promotes better learning with kinesthetic, practical experience we are allowing ourselves to have.

When we have extracted the problem, we erase all possibilities of external unknowns that live in our production system to affect what happens. Instead of relying on abstraction that tells us that some external is running and doing just what it must, instead of delving deep and finding out that it does not, and that it has another dependency that is probably just like itself, we can start anew, with full control over a certain package or a concept that we’re building.

Say, we want to test out the useContext hook works in React. Do we need to use our other, existing projects for that? Surely not. If we learn how to use this hook on itself with a simple example from documentation, then we are ready to use this knowledge for our concrete case. So, we run:

Terminal window
1
pnpm create vite@latest

choose React, TypeScript, and here we have a nice clean project:

React project structure with Vite

We can remove all unnecessary things and have a clean white screen, where we would put whatever we need, install dependencies one by one, checking. if we need, whether they have an effect upon each other, trying out a new technology and so on. Being a curious programmer, we add a library, or write some unexpected code and see what happens. We can nuke it completely and start anew, and it makes our journey so much more fun.

For the sake of not setting it up from scratch every time we choose to try something, we can have a separate folder on our machine where all these kind of projects go and live, and then we can have them as either templates to just cp them and add the things we need, or manage everything in one place, adding or removing the dependencies however we see fit.

For web development, we can have, for example, just a simple index.html + index.js files to try out Vanilla JS things, plus to that we can add a simple Node project with index.js and package.json to try out the JS quirks easily in our terminal. For other fields, we can add something we need day-to-day, to test out the new feature in Java version 777, or to follow a quick tutorial on how to make pagination work with postgresql.

To easily share our code and environment with others, there are projects dedicated to this, for example codesandbox. You can use their ā€œimport project buttonā€, but, say, we want to quickly share our GitHub repository with someone so they could tinker with the bug we found. We can go to our repo’s page, and just add a box at the end of domain name:

Changing url to githubbox shows codesandbox's icon

To share our HTML and CSS easily we can use codepen, although Python is not directly runnable in the browser, we can do it here. for all kinds of database’s shenanigans, db-fiddle can help out. We are able to fill CREATE TABLE queries alongside something we are stuck with, making the issue as small as possible, to later add the link to it in our dbastackexchange post. This way, people will be able to empirically figure out what’s wrong, instead of trying to interpret your situation the correct way. Oftentimes, when trying to reproduce issue in such an environment, you figure out not so long after starting where you have made a mistake and fix it on your own.

Trying to ensure our website’s functionality, we can setup playwright for it to run an emulated version of multiple browsers to check our project works well in them. If we don’t have a Mac, we can still use Safari through one of the many websites that run their instances and display previews, although slowly, for some cost, or even for free. If we don’t have a phone, we can either setup our Android Studio, running an emulated android, or use a website that emulates IOS, since it is impossible to do on Linux, due to proprietary nature of MacOS and XCode.

To go a little bit deeper than that, we can use Docker to emulate the environment completely to our behest: we can configure a clean Linux distribution with only the packages we want, starting from Alpine and to Arch Linux, or specific environments for our technology, like node:22. We can also use a Dockerfile with playwright installed, if we can’t use it on our Arch Linux. Having setup a small compose.yml (docker-compose.yml), we can run the needed services with a simple docker compose up <servicename>, having complete control unlike installing those on our machine, tainted by the bits of our own machine that we may not be able to control fully, or at least we don’t want to change all the time just for this one small thing.

Docker compose file with postgres service

Approaching problems like that, instead of banging our head against the wall with too much paint on it, helps us focus on what’s important, which is one of the most important aspect of our evolved brains. The world is filled with too much information, too much contradictions, but going one by one, and examining each piece of it slowly, allows us to arrive at a complex conclusion later. The systems that we build now are really complicated, and their complexity is exaggerated by outside factors like business decisions, systems that we do not control and people. When we start blank, we help ourselves and our brain run from chaos, and work out just a little bit of order that can help us stand firm on what we know and don’t know.

Thanks for reading!