Your question deserves a whole thread for itself, but I hope I can clear some fog around it.
Low technical debt mostly revolves around:
(1) Keeping your code maintainable. Example: the framework you're using (like one for making a website, or JSON API, or accepting emails and triggering actions, etc.) gets a new version. You stop what you're doing and you go and upgrade. Why? Because the older version of a framework/library you use, the less chances you have to get a bugfix from the maintainers when you inevitably stubmle upon a blocker bug one day. Being able to [almost] always switch to the newest version means:
(1.1) Keep a good collection of unit / integration tests. They don't have to cover 100% of the project. If you have a good integration test suite covering most of the customer scenarios, you're already in a much better shape than most projects on the planet.
(1.2) Keep an eye out of the changelogs and version releases of all frameworks and important libraries your project depends on. You must have a good idea what's changing in there. It might help you remove WTF code pieces that were working around a defect in the framework/library which suddenly is fixed in the newer version as well.
(2) Keeping your code evolvable -- if at certain point of development you conclude Ruby on Rails isn't cutting it, you must be brave and decisively and quickly replace it with something else. There's a huge price to pay when you're hesitant. Don't be hesitant!
If your code has separation of concerns, the various pieces have single responsibilities, if it minimizes state sharing and is regularly tested -- that's a project you won't be scared to migrate to a much newer version of your framework, or switch the framework (or even the language) entirely.
(3) Keep a huge internal documentation of what you want improved. When you're in a hurry to deliver a working product customers can use easily, you inevitably make compromises. Document them. Be ruthless and never make exceptions. No "I am too tired to write this down right now". NO! Scribble it on a napkin if you have to. Dictate it to your smartphone. Do whatever it takes. Eventually you'll return to your computer and write it down. I use a private Trello board for that, works very well for me. It has ~60 points in there but you know what? 3 months ago it had 150. ;)
So keep your code tested and documented. Be real with yourself and document (internally, for your eyes only) all of its warts. Realize its limitations. Find the value it brings to the paying customers. Maintain that. Get ideas on how it can attract more paying customers. Implement them.
Bottom line: BE ON TOP OF YOUR CODE, always. Realize that the code itself doesn't change if you don't change it (not like GitHub will maliciously modify your source files) but if you don't change it, then the business value of your code rots over time. It's really not any different compared to any other revenue-bringing asset. It needs maintenance and support (and evolution/expansion when you want to grow).
Thank you for this detailed and informative reply. I have bookmarked it and next time I see a discussion about technical debt on HN I will link to it. If my question needed its own thread, your answer needs its own stackoverflow page. Cheers.
Let me just point out that what I commented is a generalization and simply an excerpt of all the good practices I found during my [almost] 15 years of commercial experience.
Understanding your place in the world (namely what business value does your code bring) and worshipping communication -- even between your current self and your future self -- are what I found to be the gods I worship during my work, and it made me much more productive and valuable for my customers/employers.
Low technical debt mostly revolves around:
(1) Keeping your code maintainable. Example: the framework you're using (like one for making a website, or JSON API, or accepting emails and triggering actions, etc.) gets a new version. You stop what you're doing and you go and upgrade. Why? Because the older version of a framework/library you use, the less chances you have to get a bugfix from the maintainers when you inevitably stubmle upon a blocker bug one day. Being able to [almost] always switch to the newest version means:
(1.1) Keep a good collection of unit / integration tests. They don't have to cover 100% of the project. If you have a good integration test suite covering most of the customer scenarios, you're already in a much better shape than most projects on the planet.
(1.2) Keep an eye out of the changelogs and version releases of all frameworks and important libraries your project depends on. You must have a good idea what's changing in there. It might help you remove WTF code pieces that were working around a defect in the framework/library which suddenly is fixed in the newer version as well.
(2) Keeping your code evolvable -- if at certain point of development you conclude Ruby on Rails isn't cutting it, you must be brave and decisively and quickly replace it with something else. There's a huge price to pay when you're hesitant. Don't be hesitant!
If your code has separation of concerns, the various pieces have single responsibilities, if it minimizes state sharing and is regularly tested -- that's a project you won't be scared to migrate to a much newer version of your framework, or switch the framework (or even the language) entirely.
(3) Keep a huge internal documentation of what you want improved. When you're in a hurry to deliver a working product customers can use easily, you inevitably make compromises. Document them. Be ruthless and never make exceptions. No "I am too tired to write this down right now". NO! Scribble it on a napkin if you have to. Dictate it to your smartphone. Do whatever it takes. Eventually you'll return to your computer and write it down. I use a private Trello board for that, works very well for me. It has ~60 points in there but you know what? 3 months ago it had 150. ;)
So keep your code tested and documented. Be real with yourself and document (internally, for your eyes only) all of its warts. Realize its limitations. Find the value it brings to the paying customers. Maintain that. Get ideas on how it can attract more paying customers. Implement them.
Bottom line: BE ON TOP OF YOUR CODE, always. Realize that the code itself doesn't change if you don't change it (not like GitHub will maliciously modify your source files) but if you don't change it, then the business value of your code rots over time. It's really not any different compared to any other revenue-bringing asset. It needs maintenance and support (and evolution/expansion when you want to grow).