My experience is that undo/redo isn't something you tack on at the end of the project, it has to be baked into the infrastructure. Every action you take has to be delegated to an object that has all the information it needs to both do the action and undo it. Then you can just keep a stack of those objects and call them as necessary.