although a bit off-topic
A common problem I have is not planning things out, which results in me redoing a lot of code.
that's a common way to work. You cannot plan everything, it's better to have code that you can re-do (we actually call that 'refactoring').
There are not that many engineering tasks that are similar to software, but it's common in other areas (e.g. cooking or gardening) where things evolve and instead of planing everything 100%, you rather modify or replace parts of your code to improve the situation.
e.g. you could start by sticking all your units into a simple array and work on it e.g. find closest enemies, check whether bullets hit, find resources to mine etc. and if it works, it works. if it starts to become a problem (and there might be various kind of problems e.g. flexibility, performance, memory management, serializing etc.) you evolve the array to something that solves the problem.
as an example:
performance -> kd-tree
flexibility -> double linked list
memory management -> pre-allocate a big chunk instead of realloc
serialization -> use an STL container.
To avoid this, I decided to plan everything out thoroughly before I actually start
good planing is important, but don't aim for creating a perfect plan, rather put your efforts in creating easy to refactor code. Modularize it, have clean interfaces, re-use code, use well known pattern and algorithms instead of re-inventing the wheel.
So apparently rts generally use 2D game logic, even if they have 3d graphics. Is there any way to keep track of a unit's altitude using 2D logic only, and if not, how intensive would 3D game logic actually be? For example, when terrain differs sharply in altitude, how does the game make sure a unit walks over a mountain, as opposed to through it? (Not a pathing question)
for logic it's usually enough to have a heightmap as rip-off already said.
for rendering later on you can use a more detailed heightmap to pick the orientation, height etc. based on the coverage of terrain by the unit.