1. Why a Proper Setup Matters
Before diving into code, a structured setup helps you avoid “spaghetti” projects that become hard to maintain. Using a virtual environment ensures your dependencies are isolated (so you don’t clash with system Python or other projects). Having a clear skeleton (separating imports, variables, functions, and “main program logic”) gives you and future readers a map of where each piece lives. Many software engineering experts emphasize that good architecture up front can save you enormous friction later — clean structure is one of the foundations of maintainability. (Ciklum)
2. Creating a Virtual Environment πΎπ»πΉ
Here’s a typical workflow:
-
Open your terminal / command prompt in your project folder (or create a new folder).
-
Run (for Python 3):
python3 -m venv venvThis creates a directory
venv/(or whatever you name it) containing the isolated Python environment. -
Activate it:
-
On macOS / Linux:
source venv/bin/activate -
On Windows (PowerShell):
.\venv\Scripts\Activate.ps1 -
On Windows (cmd):
.\venv\Scripts\activate.bat
-
-
You’ll see your prompt change (often with
(venv)prefix). Nowpip installonly affects that environment.
Once inside, you can install packages via pip install ... and freeze them with pip freeze > requirements.txt so others (or future you) can replicate the setup.
3. Creating the .py File & Project Skeleton πΆππ‘
With your environment ready, create a file, e.g. main.py (or any name matching your application). You might also create a folder structure like:
my_project/
venv/
requirements.txt
main.py
helpers.py
data/
rates.json
tests/
test_main.py
But even a single file is fine to begin. In your .py file, you can start with a template layout — something like:
# ========================
# Imports / libraries
import os
import sys
# (other imports)
# ========================
# Global variables / configuration
API_KEY = "…" # or load from env
DATA_PATH = "data/rates.json"
SUPPORTED = ["USD", "EUR", "GBP"]
# ========================
# Functions / classes
def load_rates(path):
...
def convert(from_currency, to_currency, amount):
...
class SomeHelper:
...
# ========================
# Main program logic (entry point)
def main():
from_cur = input("From: ")
to_cur = input("To: ")
amt = float(input("Amount: "))
result = convert(from_cur, to_cur, amt)
print(f"{amt} {from_cur} = {result} {to_cur}")
if __name__ == "__main__":
main()
This layout separates concerns: imports at top, config/variables next, then functions or classes, then the “main” runner logic. It makes it easier to read, test, or expand later.
4. Detailing Each Section
Let me walk you through each “region” of that template:
-
Imports / libraries: All your
importstatements go here — standard library first, then third-party, then internal modules (if you split your code). -
Global variables / configuration: Place constants or settings (file paths, supported currencies, API keys, etc.). Avoid scattering “magic numbers” inside functions.
-
Functions / classes: These are the “units of work” — each function or class should have a clear responsibility. Prefer small, testable pieces.
-
Main program logic: This is what the script does when invoked. Wrapping it in a
main()function and guarding withif __name__ == "__main__":is Pythonic and helps you later if you want to import parts instead of always running.
This separation supports readability, easier testing, and gradual growth of your project.
5. Expert Advice & Best Practices to Begin
When starting a project, many experienced developers advise:
-
Start small, iterate: Don’t over-engineer everything at the start. Get a minimal “vertical slice” working, then refactor and expand.
-
Design for coupling & cohesion: Keep dependencies loose (low coupling) and group related behaviors (high cohesion) — a classic architectural principle. (Rheinwerk Computing Blog)
-
Prefer readability & simplicity: Over cleverness. Code is read more often than written. The simplest solution that works is often best. (Wikipedia)
-
Fix root causes when bugs appear: Rather than patching superficially, dig one layer deeper (as John Walley suggests) to prevent repeated issues. (walley.org.uk)
-
Document your decisions: Use lightweight architecture docs or “architecture decision records” so future you or collaborators know why things were done. (Medium)
These practices help your project remain maintainable as it grows.
6. Example Walkthrough: Currency Converter
Here’s how you might evolve your template into your converter:
-
Imports
import json import os -
Configuration
RATES_FILE = "data/rates.json" -
Functions / classes
def load_rates(path): with open(path) as f: return json.load(f) def convert(rates, from_cur, to_cur, amt): if from_cur != "EUR": amt = amt / rates[from_cur] return round(amt * rates[to_cur], 2) -
Main logic
def main(): rates = load_rates(RATES_FILE) fc = input("From: ").upper() tc = input("To: ").upper() amt = float(input("Amount: ")) res = convert(rates, fc, tc, amt) print(f"{amt} {fc} = {res} {tc}")
Empowering yourself with a working baseline helps you experiment, refactor, and expand without getting lost.
7. Why This Approach Works from a Software Architecture Perspective
Experts often stress that clean architectural decisions early can prevent entropy (design decay) later. Over time, ad-hoc changes can lead to rigid, fragile, or tightly coupled code. (Rheinwerk Computing Blog) By laying down clear boundaries (imports, config, logic), you prevent code from “leaking” across layers.
Also, software architects often emphasize documenting why architecture was chosen, not just what it is — the rationale helps future changes stay coherent. (Medium) Starting small and refactoring incrementally is another recommended strategy in real projects (don’t overdesign prematurely). (Scott Logic)
Finally, by modularising your code early (e.g. separating converter logic from I/O), you prepare the code for scaling — e.g., if later you build a web frontend or API, you won’t have to rewrite everything.
8. Summary & Next Steps
In summary:
-
Use a virtual environment to isolate dependencies.
-
Start with a clear file skeleton (imports, config, functions, main logic).
-
Implement a minimal working version, then refactor and grow.
-
Follow expert advice: keep things simple, design for low coupling and high cohesion, document decisions, and address root causes of bugs.
My blog post is just a jumping off point for people new to programming, I want to demystify the the world of software engineering, programming, coding, AI and various other aspects of computing.
For more detailed tutorials and educational material that's going to be far better than my blog post I recommend this website: Geeks For Geeks
Comments