I took a publishing break but not a coding one.

Since my last blog post, I’ve been deep in the trenches, building tools, breaking assumptions, refactoring logic, and learning how to code like a professional from the ground up. This isn’t just a return to blogging, it’s a recommitment to doing things right, from the beginning.

Most importantly, I’m doing all of this to instill good habits now, while the stakes are low. Even if I’m building solo, I don’t want to cut corners. Habits are hard to form later. So I’m treating my small projects like real ones with tests, structure, documentation, and consistency.


Why I Stopped Blogging (But Never Stopped Building)

In the months since my last published post, I went all-in on hands-on learning. Instead of writing about my progress, I journaled on and off but that was inconsistent. So I wanted to adopt a system that lets me document my journey inside pull requests, track it through conventional commits, and shape it through real feedback loops (tests, CI, and CLI usage).

I don’t want to just practice Python. I need to learn how to work like a developer.

I tackled classic projects and layered in modern practices:

  • Built a blog in Django with login, ownership checks, error handling, and dummy data generation.
  • Implemented secure user flows using @login_required, CSRF protection, and custom redirects.
  • Faced real-world errors like failed migrations and HttpResponse misfires and learned to debug them methodically.
  • Created a PR template to document future work, lessons, and next steps.
  • Chose to journal in PRs rather than flood my blog with every small update.

That break was the best thing I could’ve done because now I have something worth sharing and a consistent workflow to grow with.


The State of My Toolbox: Before

I started MyToolbox as a catch-all folder for practice scripts from Python Crash Course and Impractical Python Projects. Back then, it looked like this:

└── toolbox/
    ├── hello.py
    ├── hello_lists.py
    └── pig_latin.py

No structure. No tests. Just functional blobs of Python. It was useful but not sustainable.

I wanted more than working code. I wanted reliable, testable, professional code. So I upgraded.


How I Modernized the Project (And My Thinking)

Modernizing this project wasn’t just about cleaning up code it was about rewiring how I think about building software. I wanted a workflow that scales, mirrors professional environments, and encourages maintainable habits from the start. Each change below reflects a deeper shift in mindset.

1. Moved to a src/ Layout

Using a src/ structure mirrors how real-world Python packages are built and helps avoid import issues during testing. It’s a small change that builds better habits for production-ready code.

mkdir -p src/mytoolbox/utils
mv toolbox/cli_utlis.py src/mytoolbox/utils/

2. Created a Real pyproject.toml

Instead of juggling multiple config files and ad hoc dependency lists, I centralized everything into pyproject.toml. This made installation easier, enabled editable installs (pip install -e ‘.[dev]’), and set the foundation for clean dependency management and tool integration moving forward.

[project]
name = "mytoolbox"
version = "0.0.1"
requires-python = ">=3.10"

3. Pre-commit with Ruff + Black

I integrated pre-commit with ruff and black so that every commit would auto-lint and auto-format the code. This removed all style debates and guaranteed consistent formatting across the codebase before anything even hits Git.

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    hooks:
      - id: ruff
  - repo: https://github.com/psf/black
    hooks:
      - id: black

4. Testing, Coverage, and TDD

I’m using pytest and coverage.py to test core functions under the utils folder, while excluding exercises and side projects. The goal is to build a small, well-tested library I can trust.

The big shift came from enforcing a local coverage threshold:

[tool.coverage.report]
show_missing = true
fail_under = 85
include = ["src/mytoolbox/utils/*.py"]

This means I can’t commit unless coverage stays above 85%. It’s a double-edged sword great for building testing habits, but strict enough to make me think twice before skipping tests. It’s not full TDD yet, but it’s helping me get there.

5. CI/CD with GitHub Actions

To back up my local checks, I set up GitHub Actions to run tests and measure coverage on every push and pull request. This isn’t just automation it’s a second line of defense.

name: CI
on: [push, pull_request]
jobs:
  tests:
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
      - run: pip install -e '.[dev]'
      - run: pytest --cov=src/mytoolbox/utils

6. Better Docs and Commit History

Writing clear, consistent commit messages used to drive me crazy. Every time I’d stage changes, I had to stop and think: “Wait, how should I format this? Is this a fix? A chore? Do I need a scope?” It broke my flow and made me dread committing altogether.

To solve that, I adopted Conventional Commits using Commitizen. Now, the format is enforced and guided I just follow the prompts. No second-guessing. It removes friction while making my history far more readable and automatable.

In parallel, I created a pull request template that turns every PR into an async journal entry: what I changed, what I learned, and what comes next. Together, these tools give me:

  • A consistent, searchable Git history
  • The foundation for changelogs or automated releases
  • A habit of reflection baked into my workflow

Clear commits and structured PRs aren’t just about polish they’re part of how I learn in public. Each PR is like a a blog draft with context baked in.


Big Picture Lessons

HabitWhy It Matters
src/ layoutReal-world structure, better imports
Tests + TDDCatches bugs, enforces purpose
Pre-commit hooksAutomates style and sanity
CI/CDTrust your pipeline, not your memory
RefactoringGet it working, then get it clean
Journaling in PRsBlog with intention, not pressure
CLI ToolsBuild usable, not just runnable, tools

What Projects I Worked on During This Journey

🧮 Word Counter Utility

Purpose — Normalizes input text and counts word frequency
Features

  • Sorted frequency output
  • Fully tested, including edge cases

⏱️ 10K Run Timing Calculator

Purpose — Calculates average time over multiple runs
Features

  • Sentinel-controlled input loop
  • Clean, formatted output

🔢 Hex-to-Decimal Converter (No int())

Purpose — Practice manual base conversion logic
Features

  • Uses ord() and string indexing
  • Reinforces base math fundamentals

🔺 Name Triangle Generator

Purpose — String pattern creation and formatting
Features

  • Explores enumerate(), slicing, and formatting
  • Builds triangle-like patterns from user input

🎮 Hangman (Terminal Game)

Purpose — Terminal-based game logic and user feedback
Features

  • Refactored logic for clarity
  • Win/loss detection and life tracking
  • Polished CLI experience

⚙️ CLI Utilities + TDD Practice

Purpose — Build reusable input utilities with a testing-first mindset
Features

  • Centralized CLI input logic
  • Practiced writing tests before implementation
  • Used pytest.raises(SystemExit) to validate exit handling

What’s Next for CodingTides

This is the first of many pillar posts. From now on:

  • Weekly long-form blogs on projects, patterns, and philosophy
  • Micro updates logged as PRs and commit messages
  • More CLI tools, TDD practice, and useful beginner utilities

Thanks for reading, and let’s see what the tide brings tomorrow!


Join Me on My Journey

If you’d like to follow along, I’ll be sharing more about my learning process, projects, and insights here and on my GitHub repository. Feel free to connect — I’d love to hear about your coding journey too!