Como han sugerido las otras respuestas, planifique primero en papel. Comience por pensar en el problema: qué tipo de cosas necesitará representar. Por ejemplo, estoy escribiendo un programa de ajedrez, así que necesito pensar en el juego en su conjunto, los jugadores, el tablero, las diversas piezas (peones, torres, caballeros, obispos, reina, rey) y los movimientos. Estos pueden ser la base de las clases: juego, jugador, tablero, etc. Puede decidir agregar clases adicionales; en mi caso, agregué Square (un tablero es una matriz 2D de cuadrados). Luego, piensa en lo que cada clase tiene que hacer y qué información debe comunicarse entre ellos. Intenta minimizar esta información. Por ejemplo, la Junta no necesita saber cómo se mueve un Obispo; en cambio, envía un movimiento propuesto a la instancia de Obispo apropiada, y el Obispo verifica el movimiento para ver si es diagonal, y lo rechaza si no. Sin embargo, debe haber un camino claro entre dónde está el Obispo y el destino. El obispo no sabe (y no debería) saber dónde están las otras piezas en el tablero, por lo que le pide al tablero que verifique si el camino está despejado. De esta manera, el tablero no necesita saber cómo se mueven los obispos, y los obispos no necesitan saber dónde están las otras piezas.
Todo este tipo de pensamiento lo haces en papel, todavía no hay código. Lo importante es tener una idea clara de alto nivel de cómo funcionará su programa; no tiene que obtener hasta el último detalle todavía. A estas alturas ya deberías tener una idea de los métodos que necesitas para cada clase, y la información que necesitas almacenar en cada instancia de la clase, por ejemplo, cada instancia de Peón necesita saber a qué jugador pertenece. Ahora puede colocar métodos vacíos que tengan las interfaces (nombres, parámetros, valores de retorno) que trabajó anteriormente. En esta etapa, me gusta escribir comentarios en cada método que describan lo que quiero que haga el método; esto me ayuda cuando realmente escribo el código. Luego, escriba cada método; es bueno si puede probar a medida que avanza. La mayoría de los lenguajes tienen un marco para las pruebas: estoy programando el programa de ajedrez en Ruby, usando un marco de prueba llamado RSpec.
Sea flexible: sus ideas originales pueden cambiar. No tenga miedo de inventar nuevas variables locales en un método según sea necesario, siempre que la interfaz del método permanezca igual, no causará problemas. Del mismo modo, agregar variables de instancia a una clase está bien, siempre y cuando se asegure de que se inicializan correctamente cuando se crea una instancia. Si encuentra que el código en un método se está volviendo desordenado, puede ser útil refactorizar el código, colocando parte del código en subrutinas separadas. Esto es especialmente útil para operaciones que tienden a repetirse.