Why I'm Building Everything Twice Now: The GitHub Migration That Changed My Architecture

I started this migration because of a Hacker News thread about GitHub alternatives. Like most developers, I'd spent years building everything around GitHub's ecosystem without really questioning it. Three months later, what I thought would be a simple platform switch has turned into an unexpected education about software architecture—and why building for platform independence might be one of the most valuable architectural decisions I've stumbled into.
The migration wasn't driven by any particular grievance with GitHub. I was genuinely curious: how hard would it actually be to move? Everyone talks about vendor lock-in, but I wanted to see what it looked like in practice.
The Migration Decision
Rather than picking one alternative and going all-in, I decided to test the waters with a dual approach. I set up accounts on Codeberg (the European nonprofit Git hosting service) and spun up my own self-hosted GitTea instance. The plan was simple: new projects would start platform-agnostic, existing ones would get gradually migrated, and I'd keep GitHub mirrors for projects that needed the visibility.
What I didn't expect was how this "building twice" approach would fundamentally change how I think about project architecture.
What Platform Independence Actually Looks Like
The first shock was discovering how many GitHub-specific assumptions were baked into my workflows. I'm not talking about obvious things like GitHub Actions—I expected that migration pain. I'm talking about the subtle dependencies I didn't notice until they broke.
My CI/CD pipelines were the biggest wake-up call. I thought I was being smart by using GitHub Actions, but when I tried to replicate those workflows elsewhere, I realized how much I was leaning on GitHub's specific environment variables, secret management, and runner configurations. What looked like "infrastructure as code" was actually "infrastructure as GitHub."
Then there were the documentation dependencies I'd never considered. README files that linked to GitHub's issue tracker, wikis that assumed GitHub's markdown rendering, project badges that only worked with GitHub's API. These weren't architectural decisions—they were convenience choices that had calcified into dependencies.
Architectural Revelations
Here's what surprised me: when I can't rely on platform features, I build more portable systems. And portable systems, it turns out, are often better systems.
Take my Docker compositions. On GitHub, I'd gotten lazy about environment configuration because GitHub's secrets management made it easy to inject variables at runtime. When I moved to platforms without that specific feature set, I had to design proper configuration management from the start. The result? My containers became more predictable and easier to debug locally.
My deployment scripts got similar treatment. GitHub Actions had trained me to think in terms of GitHub's specific runner environment. When I started building for multiple platforms, I had to write scripts that worked regardless of the host system. The cognitive overhead was higher initially, but the scripts became more robust and easier to troubleshoot.
The Unexpected Benefits
The most interesting change has been in how I approach separation of concerns. When I can't lean on platform integrations, I'm forced to think more carefully about which responsibilities belong to my code versus my infrastructure.
I've started building cleaner CI/CD pipelines that work anywhere. Instead of GitHub Actions that assume specific GitHub features, I write shell scripts that can run on any POSIX system, then wrap them in whatever platform-specific CI syntax is needed. It's more work upfront, but the pipelines are more testable and portable.
There's something satisfying about truly owning my development pipeline. When I push to my self-hosted GitTea instance, I know exactly what's happening at every step. No mysterious delays, no service outages I can't control, no feature changes that break my workflows without warning.
What I'm Learning About Architecture vs. Convenience
The migration has made me more aware of the difference between architectural decisions and vendor conveniences. GitHub Issues isn't solving an architecture problem—it's providing a convenience. Same with GitHub Pages, GitHub Packages, and most of the GitHub ecosystem.
The real architecture problems are things like: How do I manage configuration? How do I handle secrets? How do I structure my deployment pipeline? These problems exist regardless of which Git host I use, and solving them in a platform-agnostic way often leads to better solutions.
Platform-agnostic thinking has changed how I structure repositories too. Instead of organizing projects around GitHub's conventions, I organize them around the actual software architecture. The .github directory becomes one deployment target among many, not the central organizing principle.
Current Reality Check
Three months in, the dual-platform setup is working better than I expected. New projects start clean—no GitHub assumptions, proper configuration management from day one, CI/CD that works anywhere. The cognitive load of maintaining multiple platforms is real but manageable, especially since the architectural improvements reduce complexity elsewhere.
Some projects migrated easily. Simple libraries with minimal CI/CD requirements moved over with just a git remote change. Others revealed deeper dependencies I hadn't noticed—projects that relied heavily on GitHub's API integrations or had documentation scattered across GitHub-specific features.
The performance differences are interesting. My self-hosted GitTea instance is noticeably faster for day-to-day operations, probably because it's running on dedicated hardware instead of shared cloud infrastructure. Codeberg feels similar to GitHub in terms of responsiveness, though their web interface is more minimal.
The Questions This Raises
The experience has left me wondering: how much of what we consider "modern development architecture" is actually just vendor lock-in we've normalized? When I look at projects built entirely around GitHub's ecosystem, I see architectural decisions that aren't really decisions—they're just the path of least resistance within one platform's feature set.
What would software development look like if we designed for portability first, convenience second? I'm starting to think the answer might be: better software, built on stronger architectural foundations, with more thoughtful separation between business logic and infrastructure concerns.
The migration isn't complete, and I'm still learning. But building everything twice has already taught me more about software architecture than years of staying comfortably within one platform's walls.