Maintenance of legacy systems is a laborious, error-prone task. It is often difficult to define encapsulated components in procedural programs. We define a comprehensive process for re-engineering procedural, legacy code to an object-oriented architecture. The process is based on a program representation graph, called a statement dependence graph. The process includes a technique to recognize potential object hierarchies, state variables and operations. Procedures are partitioned into operations by analyzing variable use-def chains. The statement dependence graph is restructured by merging cohesive parts of the graph to produce a restructured graph. From the restructured graph, we identify hierarchies of objects. The process to encapsulate the objects includes streamlining the interfaces.