I also hate the "cities are automatically super-fortresses" approach in the game and have been working on some changes to remedy this. Here is where I've gotten to so far:
Code:
<Defines>
<Update>
<Set Value="13"/>
<Where Name="MAX_HIT_POINTS"/>
</Update>
<Update>
<Set Value="100"/>
<Where Name="CITY_STRENGTH_UNIT_DIVISOR"/>
</Update>
<Update>
<Set Value="0"/>
<Where Name="CITY_ATTACK_RANGE"/>
</Update>
</Defines>
UPDATE Buildings SET Defense = Defense * 1.5 WHERE Defense > 0 ;
MAX_HIT_POINTS makes raging barbarians very troublesome (coupled with the loss of city bombardment), and extends other combat a bit more since units can take more punishment, which makes bombardment a bit less devastating.
CITY_STRENGTH_UNIT_DIVISOR is used when calculating how much the defense is changed by the garrison. The default is 500. This makes cities less fortress like on their own, and makes it especially important later in the game that they be garrisoned. A fully defended city can have a defense of around 100 with all the buildings and a mech inf defending it. One really nice aspect of this one is the option to counterattack out of the city, which now that the defender can be a significant portion of the city defense, is more important now.
The line of SQL code emphasizes the importance walls/castles in making a city defensible. This perhaps is not strong enough, but for 1 line is a good start.
There are more things that could be changed, like the tech exponent, that policy that adds 33% defense, etc..