Tinselcity

Nota: Utilizo la palabra problema en el sentido general de la siguiente acepción incluida en el DRAE:

Planteamiento de una situación cuya respuesta desconocida debe obtenerse a través de métodos científicos.

Así un problema para nosotros será en general cualquier situación en la que nos planteemos un cierto objetivo a alcanzar por medio de la aplicación de -probablemente- algún sistema de software, que es lo que tendremos que desarrollar o programar.

Llegan los problemas

A la hora de resolver un problema, los primeros pasos son, la mayoría de las veces, los más importantes. El primero de todos ellos debe ser el análisis y comprensión del propio problema.

En tiempos recientes, cualquier idea que suene o parezca sonar a “análisis” o a “fases” o “pasos” parece que encuentre una cierta oposición. Esto es debido sobre todo a quienes aplican y entienden mal las ideas de las metodologías llamadas Agile 1). Como ya he dicho en alguna ocasión no tengo intención de tratar estos temas, pero es conveniente señalar que ese tipo de metodologías no son realmente metodologías de programación, sino que se centran en la gestión del desarrollo y en dinámicas de gestión de equipos. Nosotros estamos hablando específicamente de programar, en una perspectiva que asumimos es fundamentalmente personal. Esto no quiere decir que sea uni-personal, pero sí que es importante resaltar que nos estamos moviendo a un nivel completamente diferente.

La comprensión del problema seguramente sea la fase más importante de todo el proceso y la que más retorno proporciona sobre el tiempo que invirtamos en ella. En el fondo, si no hemos llegado a comprender correctamente un problema, ¿cómo podemos siquiera pretender ser capaces de resolverlo? Por tanto, es una tarea crítica y deberemos dedicarle todo el esfuerzo que sea razonable dedicarle. Más adelante, cuando hablemos de “economía del código” veremos con más claridad cuánto es razonable o no dedicar a una tarea. Por ahora quedémonos con la idea de que el análisis y comprensión del problema, no solo es el primer paso sino probablemente el más importante.

Análisis

Existen muchas técnicas más o menos diferentes o similares para el análisis, y todas ellas tienen abundantes explicaciones y guías disponibles. Yo voy a contar cómo me enfrento yo al análisis de los problemas desde el punto de vista de la programación. No creo que sea una forma intrínsecamente mejor que otras, pero a mi me funciona y no es algo esencialmente diferente o innovador respecto a lo que he visto que hacen otras personas.

Lo que sigue no debe entenderse como un orden particular. Son varias tareas u objetivos a alcanzar, pero generalmente se llevan a cabo de manera más o menos simultánea.

Identificación de las partes

En todo problema podemos encontrar diferentes partes o elementos que intervienen. No se trata solo de identificarlos como tales, sino de, a la vez que lo hacemos, identificar su relevancia y su papel en el problema y relación con otras partes.

Tomemos por ejemplo el caso de que nos planteemos desarrollar un juego como el Parchís. Aquí podríamos identificar muchas partes diferentes. Inicialmente podemos ver algunas con cierta claridad: Jugadores, tablero, fichas, casillas, dados… Algunas no serán tan visibles o evidentes, como por ejemplo el concepto del turno. El turno no es algo palpable, pero parece por lo menos en primera instancia considerar que de algún modo gestionar y seguir el turno es relevante dentro del juego.

Podemos caer, sin querer, en una situación en la que aparentemente empecemos a tener decenas, cientos, miles de partes. El problema se vuelve más complejo cuantas más partes diferentes consideremos. Por eso mismo, es importante considerar las siguientes dos tareas del análisis.

Es interesante también no pensar solo en “partes” como elementos directos del problema. Como nombres. Quiero decir con esto que estas “partes del problema” no solo son cosas que intervienen, también pueden ser cálculos, procesos, acciones, etc, que descubrimos que habrá que hacer. El caso del turno que mencionaba antes. Podemos pensar en el turno como un elemento, pero en realidad es más una mecánica, un proceso o una gestión que interviene en el problema.

En el próximo capítulo, donde veremos cómo analizar algunos casos prácticos como ejercicio, intentaré cubrir también esta idea.

Definir niveles de detalle

Casi cualquier problema que nos encontremos, se podrá contemplar desde diferentes perspectivas o en diferentes niveles de abstracción. A mi, generalmente, me gusta llamar a esto niveles de detalle. En parte porque creo que es más fácil de entender para quien no esté acostumbrado y en parte también porque creo que recuerda constantemente dónde está la clave para definir un determinado nivel: el tamaño del detalle con el que lo consideramos.

Me recuerda también, una técnica que hay en videojuegos y en pintura, y en general en casi todos los sistemas de representación un poco elaborados. Seguramente os resultará familiar. Se trata simplemente de representar con mayor detalle las cosas que están cerca y con menos las que están lejos. Me recuerda la historia de Goya y los techos de la Basílica del Pilar de Zaragoza. Se cuenta que Goya pintaba muy rápido a base de aprovecharse de que la altura de techos hacía que las pinturas siempre se verían desde lejos y por tanto, no necesitaba dedicar tanto esfuerzo a los detalles más pequeños puesto que no se iban a ver.

Necesitaremos definir siempre un primer nivel de detalle de visión de conjunto. Aquí definiremos muy poco detalle y solo consideraremos un pequeño número de partes claras y relevantes. No tenemos que preocuparnos por analizar cada una de estas partes, sino identificar 3, 4, quizá media docena, de elementos fundamentales y su papel general dentro del problema. Siguiendo el ejemplo del Parchís podríamos considerar, por ejemplo, jugadores (así en conjunto), tablero, fichas, turno de juego. No consideramos, por ejemplo, las casillas individuales. Puede que estas terminen siendo una entidad concreta en nuestro problema, pero a este nivel consideramos que las casillas son parte del tablero. No consideramos los dados tampoco, porque es más bien un mecanismo concreto para decidir el movimiento. Podríamos decir que son un detalle de implementación.

Además de esto, es habitual, aunque dependerá de cada caso, definir algunos niveles de detalle más. En muchos casos, estos niveles no solo son más detallados, sino que solo se centran en una parte concreta del problema. De ahí que consideremos la siguiente de las tareas del análisis.

Descomposición

Existen problemas de muy diferentes alcances, como es obvio. Algunos son tan triviales que se alcanzan a comprender con una única visión de conjunto y apenas intervienen en ellos una o dos partes. Pero esto, en general, no será lo común.

La mayoría de las veces nos encontraremos problemas de una complejidad alta, con muchos detalles y un número relativamente grande de partes diferentes y muchas relaciones entre ellas. La herramienta más potente de que disponemos para analizar este tipo de situaciones es utilizar la descomposición.

Se trata simplemente de separar diferentes elementos, limitando su alcance a una parte o aspecto más pequeño del problema entero, de modo que nos permita analizar cada uno de la forma más independiente posible. Hacer esto bien es casi un arte en el sentido de que requiere sobre todo experiencia y el desarrollo de una cierta intuición o habilidad para identificar esas partes. Personalmente la descomposición se ve reflejada en uno de los dos principios fundamentales que yo me planteo en la programación:

Junta lo que va junto. Separa lo que va separado.

Esto, que también aplico al propio código, es una expresión directa de las tareas de descomposición de problemas. Es cierto que no siempre es fácil delimitar qué va junto o separado. Una forma más o menos eficaz de establecer las separaciones consiste en observar las relaciones que hay entre las partes. Partes separadas pueden, como es natural, tener relaciones entre ellas, pero generalmente estas suelen ser pocas y claras, bien definidas. Si observamos que dos cosas que considerábamos independientes, tienen múltiples puntos de relación, dependencias que van en ambos sentidos (la parte A depende de B y la parte B depende de A), es bastante posible que no vayan tan separadas como creíamos.

En ocasiones lo que observaremos es que todas las relaciones de una de las partes son exclusivamente con otra de las partes. Si vemos algo así, es bastante probable que en realidad sean dos partes de una misma cosa o, visto de otro modo, que hayamos intentado descomponer una unidad a un nivel de detalle demasiado bajo. La descomposición debe hacerse siempre teniendo en cuenta el nivel de detalle que estemos considerando en cada caso. En el ejemplo del Parchís, recordemos que inicialmente no habíamos considerado las diferentes casillas como un elemento relevante. Sin embargo, una vez descompuesto el problema general, cuando examinemos un nivel de detalle enfocado exclusivamente en el tablero, entonces será razonable considerar que a ese nivel las casillas sí son relevantes y tienen entidad propia.

Dimensionar el problema

Un punto particularmente interesante a la hora de analizar un problema es dimensionarlo correctamente. En muchos casos, la validez o idoneidad de una solución depende directamente de alguna magnitud del problema que podemos llamar “tamaño”.

Una pregunta que me tocaba preguntar muchas veces cuando me contaban un cierto problema o proyecto era: “Pero ¿cuántos X hay?”. Por ejemplo:

Necesitamos un sistema para buscar unas palabras clave en un montón de textos.
Pero ¿cuántos textos son “un montón”? ¿50, 5000, 5 millones? Y ¿cuántas veces vais a buscar cada día?

O:

Queremos crear un servicio en el que cualquier usuario pueda consultar los libros que ha pedido prestados de la biblioteca.
Pero ¿cuántos usuarios tenéis? ¿Cuántos libros toma prestados, de media, cada uno? ¿Cuántos van a consultar este servicio a la vez?

Cuántos, cuántos, cuántos… Lo que queremos hacer lo tenemos claro, es relativamente fácil de analizar. Pero es igual de importante analizar las dimensiones del problema. Estas dimensiones vana determinar muy directamente muchas de las necesidades que tengamos luego a la hora de implementar o incluso de plantear y valorar una solución u otra.

Y no es solo una cuestión económica, o de cuántos recursos dediquemos. Algunos contestan a estas cuestiones demasiado deprisa con un descuidado “pondremos más máquinas” o más memoria, o más lo que sea. Y hay dos pegas a esto. Una es que todo eso cuesta dinero, por supuesto. Pero la pega más importante es que algunas soluciones pueden resolver el problema solo para algunos tamaños, y en algunos otros casos dependiendo del tamaño algunas soluciones pueden funcionar peor que otras.

Conocer las dimensiones del problema es una parte fundamental del análisis del mismo. No solo nos va a permitir elegir mejor la solución, a veces también nos hará ver particularidades y detalles del problema que pueden ser muy relevantes.

Problema vs Solución

El punto fundamental, la clave de esta fase, y lo que debemos mantener siempre en nuestra mente cuando realizamos la tarea de análisis es que problema y solución son dos cosas completamente diferentes.

Al hacer un análisis, tenemos que centrarnos por completo en observar y comprender. Este es el objetivo único de la tarea. Queremos establecer qué es lo que queremos resolver. En ningún momento tenemos que dejarnos llevar a dar el salto y ponernos a plantearnos cómo lo vamos a resolver.

Por esto, cuando hablamos, por ejemplo, de identificar partes o relaciones o de establecer niveles de detalle, nunca he mencionado nada específico sobre qué, por ejemplo, cómo vamos a implementar una cierta relación o qué va a implicar manejar un cierto elemento. Eso son temas que pertenecen a la solución, no al problema. Al cómo, no al qué.

Insisto en recalcar la importancia de esto.

Si nos dejamos llevar y mezclamos el qué con el cómo, es muy fácil que caigamos en uno de estos errores, o en ambos:

  • Es fácil que ignoremos información del problema. Si nos acercamos con una idea preconcebida de cómo vamos a solucionarlo, terminaremos por, consciente o inconscientemente, tratar de adaptar el problema a nuestra solución en lugar de lo contrario, que es lo que debemos hacer.
  • Es también fácil que terminemos asumiendo algunas partes de nuestra solución como parte del problema original y esto nos dificulte contemplar otras soluciones que pueden ser tan válidas o incluso más. Además, como es evidente, nadie quiere que la solución pase a ser parte del problema ;)

Diagramas, esquemas, notas

Cuando yo hago este análisis, suelo terminar tomando algunas notas y a veces haciendo algún tipo de diagrama o esquema. Estos son solo una herramienta más en el análisis. No debemos entender que nada de ello es el producto del análisis, ni que estos apuntes son algún tipo de documentación que podamos compartir.

El producto del análisis del problema es nuestro conocimiento, es la comprensión del problema. Las notas o diagramas que hagamos son únicamente para ayudarnos a nosotros. Esto es porque son notas que tomamos mientras aún estamos haciendo el análisis. Así, no tenemos una comprensión completa todavía, y cualquier nota o diagrama contendrá, muy probablemente, pensamientos parciales, correcciones, anotaciones temporales, o muchas veces símbolos que sirven como indicación para el conocimiento que está, en realidad, en nuestra cabeza.

Si lo que queremos es crear algún tipo de documento (sea escrito o en forma de diagrama o esquema) que sirva para comunicar o explicar el problema a otros, entonces lo tendremos que crear de forma explícita y esto será después de haber adquirido nosotros una comprensión suficiente del problema, es decir, después de haber realizado el análisis, no mientras aún lo estamos haciendo.

Por esto mismo, recomiendo que no nos preocupemos demasiado de estas notas, más allá de lo que nos sirva a nosotros mismos. Por ejemplo, no es necesario utilizar ningún tipo de formato o de notación específicos o formales.

1)
¿O quizá, lamentablemente, de quienes las entienden y aplican bien?

Discusión

Escribe el comentario. Se permite la sintaxis wiki: