Guido van Rossum y su «Programación para todos» (Carlos Fontela)

septiembre 18, 2009

Se acaban de cumplir diez años del paper “Computer Programming for Everybody”, de Guido van Rossum, en el que aboga por enseñar programación de computadoras a toda la población, y propone utilizar Python para ello.

En ese artículo, van Rossum dice que, en su opinión, debería enseñarse a programar en la escuela primaria, tanto como se enseña aritmética y a leer y escribir.

¿Por qué enseñar programación a no informáticos?

Según Guido van Rossum, la enseñanza de computación pensando en el uso de la herramienta no es suficiente. Él aboga por enseñar a la gente a programar las computadoras.
Las razones de más peso que justificarían esto son:

  • Va a ayudar a que la gente aprenda a pensar mejor, de una manera más lógica. Esto es razonable, debido a que la programación de computadoras es una aplicación de la resolución de problemas. Una década y media antes, el proyecto Logo iba también en esta dirección.
  • Mejorará el uso de las computadoras por parte de la gente, al entender cómo funcionan. El ejemplo que él cita es bastante elocuente: cuando algo falla en mi auto, muchas veces podemos intuir en dónde puede estar el problema, precisamente porque sabemos algo del funcionamiento de los autos (el único problema con el ejemplo es su anacronismo, pues hoy en día la gente entiende más a las computadoras y menos a los autos, que cada vez tienen más software embebido).
  • Permitirá que la gente programe sus aparatos para que hagan lo que ellos quieran. De nuevo, el ejemplo es algo anacrónico, aunque visionario: dice que si los celulares fueran programables se podría hacerlos probar sucesivamente diferentes números para ubicar a una persona.

Una vez que todos sepan programar, desarrollar programas será tan habitual como leer y escribir. Y si bien no todos van a construir buenos programas, lo cierto es que no todos los que escribimos somos buenos novelistas. Pero al menos tenemos las herramientas para darnos a entender por medio de la escritura.
Van Rossum dice que lo cierto es que la gente tiene algunas aplicaciones programables, como las planillas de cálculo y algunas bases de datos simples, pero restringidas a una forma matricial o tabular que no aplica a la resolución de cualquier problema.

Si bien su aspiración de máxima es que todos programen, y que, como las primeras letras y la matemática, se enseñen desde la escuela primaria, propone empezar de manera más conservadora, con estudiantes secundarios y universitarios de carreras no técnicas. En particular, a mí esta no me parece una buena idea, por las resistencias que generaría.

Él sugiere que Python, por su simplicidad, sería la herramienta ideal para lograr este objetivo.

Enseñanzas de la historia

La verdad es que la historia nos muestra que hubo, de maneras no exactamente iguales pero sí parecidas, varios intentos en la línea de lo de van Rossum.

Al fin y al cabo, el primer lenguaje de propósito general que logró gran aceptación, Fortran, fue presentado en sociedad como una forma de programar computadoras al alcance de todos. Se decía que la gente ya no tendría que programar, sino sólo “escribir fórmulas”, y ¿quién no sabe escribir fórmulas? Puede que a nosotros, 50 años más tarde, nos llame la atención el grado de candidez de estas afirmaciones, pero así se plantearon.
Al poco tiempo, dado que no todos sabían trabajar con fórmulas, se decidió crear un lenguaje de aspecto menos aritmético, que fuera una especie de inglés coloquial. Y adivinen qué nació: ¡Cobol, el lenguaje que incluso podría ser usado por abogados para programar computadoras!
No a todos les gustó Cobol. Por eso, los amantes de Fortran que quisieron acercarlo a la gente común, decidieron crear un lenguaje que fuera un subconjunto simple de Fortran, especialmente diseñado para la enseñanza. ¡Y así surgió Basic!

Mis lectores tal vez opinen que esto es muy distinto de la propuesta de van Rossum. Yo lo único diferente que veo es el tiempo que pasó entre aquellas primeras ideas y el año 1999.

En cuanto a la enseñanza en sí, también ha habido intentos.
Me acuerdo cuando en los años 1980 comenzó el proyecto Logo, mucho más es una filosofía de educación que un lenguaje. En realidad, con Logo no se pretendía enseñar a programar computadoras, sino a resolver problemas de una manera lógica. Por eso mismo no tenía mucho sentido la disyuntiva que muchos planteaban entre Logo y Basic.
Scheme también fue presentado como un lenguaje muy natural para enseñar a programar, pero quienes lo plantean lo hacen desde la perspectiva de usarlo como primer lenguaje de alguien que va a hacer una carrera informática. De hecho, requiere bastante formalismo matemático, lo que lo hace no apto para una audiencia masiva.

Mi visión

Lo que yo veo es que la preocupación por lograr que la gente pueda programar ha surgido generalmente de la propia comunidad informática. No conozco a ningún no informático que esté interesado en aprender a programar.
¿Por qué ocurrirá eso? ¿Estamos preocupados por ser incomprendidos? ¿Será una especie de frustración al ver que nuestros amigos abogados, ingenieros civiles y médicos pueden contarles a sus abuelitas en qué están trabajando, y nosotros no? No lo sé… pero sería interesante averiguarlo.

El otro tema es que en estos últimos 10 años, mal que le pese a van Rossum, poco ha cambiado: la programación sigue siendo un campo para los especialistas. Tal vez, y ojalá, Guido van Rossum tenga razón, pero por el momento no hay muchos indicios de que se vaya a producir un cambio.

Y respecto de enseñar a pensar de manera lógica, creo que el proyecto Logo estaba mejor orientado, y sin embargo fue sepultado por quienes dos grupos diferentes de personas:

  • Los que decían que eso no era programar computadoras, y por lo tanto carecía de seriedad.
  • Los que decían que eso era programar computadoras, y ¿para qué quería un chico aprender a programar?

Negociación y proactividad, parte II (Cristian Pascuzzi)

septiembre 8, 2009

En la primera parte de este articulo, hablamos de la Proactividad. Ahora vamos a tratar de establecer una relación mas directa con nuestras negociaciones.

Hace poco leía un artículo de Rita Mulcahy, PMP, “Tricks of the Trade® for Dealing With Anger in Project Management” (Trucos del Oficio para manejar la Ira en Administración de Proyectos), donde habla de tomar conciencia que la Ira se genera en nosotros, no que otros nos la generan.
Podemos pensar que no tiene que ver, pero ese tipo de pensamientos nos retiene en la posición “la causa del problema siempre está afuera”, en lugar de “yo puedo hacer la diferencia”

Entonces, para negociar es bueno:

  • Saber que podemos manejar nuestras reacciones
  • Anticiparnos a los hechos

Pongamos un ejemplo: estamos negociando el aumento del alquiler del departamento donde estoy, mi interés es que no me aumenten y el del dueño del departamento es aumentar.
A simple vista esta negociación es inútil: yo puedo pensar que no tengo oportunidad de lograr que no me aumenten, y/o que me tengo que me tengo que buscar otro departamento.
Pero puede suceder que en una preparación previa (de esto se trata la negociación), revise mi postura, la posible postura del dueño (porque realmente no se que piensa el otro, puedo suponerlo) y pueda presentar opciones para la negociación.
En el libro “Si, de acuerdo” los autores proponen revisar estos puntos y anticiparse de alguna manera a la negociación. Presentar alternativas de negociación, pensar los puntos no negociables, o desde donde ya no conviene negociar, nos ayudara a mejorar los resultados de la negociación.
En el ejemplo del alquiler, es posible presentar la buena conducta como inquilino (sabiendo que es un bien preciado por los dueños), el mantenimiento del departamento por mi parte o la falta de parte del dueño, la ubicación, los alquileres por la zona para un departamento del mismo tipo, los problemas surgidos en el edificio, etc.
También, se pueden presentar alternativas como aumentos menores, escalonados, ajustados a determinados índices externos, ofrecimiento de mano de obra el mantenimiento del departamento que debería estar a cargo del dueño, etc.
Si pensamos previamente a la reunión que no podemos hacer frente o no estamos dispuestos a pagar un aumento mayor a X, podemos ponerlo sobre la mesa de negociación al comienzo. Una técnica usada a menudo, y no muy recomendada es la del regateo: yo parto desde la negación del aumento y voy aceptando parcialmente un aumento mientras el dueño va bajando su porcentaje.
La práctica recomendada es la de poner las cosas claras al comienzo de la negociación, saber cuales son los intereses propios y estimar los intereses de la otra parte, anticiparse, proponer opciones, y saber cuando se termino la negociación.

En resumen:

  • Podemos mejorar el resultado de la negociación, reunión, trabajo en equipo, etc., sabiendo que podemos y debemos controlar nuestra reacción a los estímulos externos (no enojarnos cuando no obtenemos el resultado esperado, alguien nos agrede, etc.).
  • Adelantarnos a la negociación pensando en nuestros intereses, suponer los intereses de la otra parte, analizar alternativas de solución, y hasta donde negociaremos.

Negociación y proactividad, parte I (Cristian Pascuzzi)

agosto 31, 2009

Esta vez voy a tratar de profundizar un poco más el tema de la proactividad o autoconciencia.
La palabra proactividad se usa generalmente para definir acciones anticipadas a hechos o la actitud de actuar sin esperar estímulos. Por ejemplo, es común escuchar a los directivos decir: “hay que ser proactivos y buscar mejoras en el sistema….”
Podemos traducirlo en “Tener Iniciativa”.
También podemos pensarlo de otra manera y no por ello deja de ser válida la casi definición anterior, sino que la completa: Proactividad significa que como seres humanos, somos responsables de nuestras vidas.
Esta definición es muy general, pero apunta a algo muy importante: Nosotros tenemos la capacidad de decidir la reacción a cualquier estimulo externo.

Sacándonos de encima la teoría determinista

Esta teoría dice que las reacciones son originadas por la genética, la cultura y/o el ambiente. Pero la falencia principal de esta teoría es que está basada en pruebas realizadas en animales y personas neuróticas y psicópatas.
Por ejemplo, es un clásico el experimento de Pavlov del perro que babea al escuchar una campana, que en eventos anteriores el científico se encargo de asociar a la comida.
Si lo pensamos seriamente, creo que esta teoría no nos representa como seres humanos.

¿Entonces?

Covey, en su libro “Los siete hábitos de la gente altamente efectiva” descompone la palabra Responsabilidad: “Habilidad para elegir la respuesta”.
Muchos podrán decir que no tienen incidencia sobre su accionar, o que no tienen opción. Podemos revisar las experiencias de Víctor Frank (psicólogo creador de la Logosofía) en los campos de concentración, o algo más cercano, las experiencias de los sobrevivientes de la tragedia de los Andes: Aparentemente, ellos no tenían opción, pero ellos la buscaron, la siguieron, se responsabilizaron…

¿Para qué nos sirve ser proactivos en la relación con otros?

La proactividad nos permite mejorar los resultados de:

  • Las reuniones
  • Las negociaciones
  • El trabajo en equipo, con clientes, con proveedores, con compañeros de trabajo
  • La coordinación de proyectos

Hace poco leía un artículo de Rita Mulcahy, PMP, “Tricks of the Trade® for Dealing With Anger in Project Management” (Trucos del Oficio para manejar la Ira en Administración de Proyectos), donde habla de tomar conciencia que la Ira se genera en nosotros, no que otros nos la generan.
Podemos pensar que no tiene que ver, pero ese tipo de pensamientos nos retiene en la posición “la causa del problema siempre está afuera”, en lugar de “yo puedo hacer la diferencia”.

A qué llamamos Ingeniería de Software (Carlos Fontela)

agosto 26, 2009

Hace unos días, como dijimos en otro artículo, surgió una polémica sobre la Ingeniería de Software que está lejos de aquietarse. Me hace acordar a aquél otro artículo de Dijkstra, escrito en 1988, en el cual decía que la Ingeniería de Software era “la disciplina condenada”. Recordemos que, más allá del prestigio de Dijkstra, 18 años después seguimos hablando de Ingeniería de Software.
Ya Marcio ha escrito sobre este tema un artículo en este mismo blog, con el que coincido en casi todo. Por eso, yo me voy a centrar en un solo aspecto: ¿a qué llamamos “ingeniería de software”? Porque convengamos que, como el mismo Dijkstra decía, “la popularidad de su nombre es suficiente para hacerla sospechosa”.

¿Ingeniería => Industria?

A mi juicio, uno de los problemas que tiene la ingeniería de software es que, una vez que se empezó a hablar de ella, se pasó a mencionar la existencia de una supuesta “industria del software”.
Y si bien estoy de acuerdo con la existencia de una ingeniería del software, dudo un poco cuando se plantean discusiones sobre la supuesta industria del software.

Vamos por partes. ¿Es el software un producto? Si entendemos por producto el resultado de algo que se produce, que es derivado de una actividad humana, sí, podemos hablar de productos de software.
Y el hecho de que haya “productos de software”, ¿implica la existencia de una industria del software? Bueno, aquí vuelven a surgir mis dudas… depende de a qué llamemos “industria”.

En realidad, hay distintos tipos de industrias (aclaro que mi clasificación no es ni rigurosa ni exhaustiva: estoy tratando de explorar ideas, solamente):

  • Existen las industrias manufactureras de línea de producción, en las cuales se diseña un producto y luego se lo fabrica en forma repetitiva, poniendo el foco en la mejora continua del proceso de producción para lograr calidad, rentabilidad, etc. La industria automotriz, la de electrodomésticos, las de alimentos y bebidas, entre muchos otros, son casos particulares de este tipo de industrias.
  • Existen industrias de proyectos, en las cuales se diseña un producto y luego se lo construye por única vez, en base a un plan de construcción. La industria típica de este tipo es la industria de la construcción (edificios, plantas industriales, puentes, caminos).
  • Existen también, o al menos se las ha dado en llamar así, las industrias culturales. De hecho, las películas, las piezas musicales o los libros también son productos de una actividad humana. En este caso, se trata de un producto que se diseña y se construye por única vez, como en el caso anterior, pero en el cual hay mucha más experimentación sobre la marcha, y en el cual la construcción es realizada por gente muy especializada.

¿Y el software? Si admitimos que estas tres actividades son industrias, entonces el desarrollo de software también puede serlo.
El problema viene con que la mayor parte de la gente, cuando oye hablar de la industria del software, piensa en industrias manufactureras de línea de producción. Y los que desarrollamos software sabemos que, en realidad, el mismo está mucho más cerca de las industrias de proyectos, con parecidos nada desdeñables con las industrias culturales.

Esto no parece ser visto, ni por los impulsores, ni por los críticos de la Ingeniería de Software que, creo yo, es una ingeniería de pleno derecho.
Y si no, que la Ingeniería Civil también deje de llamarse así (antes de que me ataquen, aclaro que yo mismo soy Ingeniero Civil).

Ingeniería de software y administración de proyectos

Otra cuestión indeseable que surge al hablar de Ingeniería de Software, es que muchos lo asocian a administración de proyectos de desarrollo de software. Lamentablemente, esto es inevitable, ya que el concepto de Ingeniería de Software se acuñó hace 40 años, y este fue un aspecto en el que se puso mucho énfasis.
Pero también se puso énfasis en captura de requisitos, análisis, diseño, pruebas, etc., y nadie parece acordarse de estas sub-disciplinas de la Ingeniería de Software, salvo para analizarlas desde la perspectiva de la administración de proyectos.

Y es una lástima, porque, además de la administración de proyectos de desarrollo, son incumbencias de la Ingeniería de software, temas tan variados e interesantes como (de nuevo, los temas y disciplinas no pretenden ser ni exhaustivos ni rigurosos):

  • En la disciplina de Captura de requisitos: casos de uso, user stories, granularidad de los requisitos a lo largo de un proyecto, etc.
  • En la disciplina de Análisis: diseño por dominio, análisis funcional, patrones de análisis, especificaciones formales, etc.
  • En la disciplina de Diseño: patrones de diseño, refactoring, Test Driven Development, diseño por contrato, diseño de bases de datos, modularización, patrones enterprise, integración, etc.
  • En la disciplina de Construcción: complejidad algorítmica, tamaño del sofware, legibilidad de código, reutilización, frameworks, etc.
  • En la disciplina de Control de calidad: pruebas, revisiones por pares, automatización, validación y verificación, etc.
  • En la disciplina de Metodología: métodos por etapas, métodos iterativos, métodos ágiles, “peopleware”, desarrollo en equipos heterogéneos y a distancia, el medio ambiente del desarrollo, etc.
  • Y en la propia disciplina de Administración de proyectos: estimaciones, con sus métodos, técnicas de seguimiento de proyectos, control de cambios, métricas, etc.
  • Más todos los temas asociados al desarrollo de software, independientemente de cómo los clasifiquemos.

Creo que queda claro que considero totalmente válido hablar de Ingeniería de Software… Y si no fuese así, sería muy incoherente respecto del nombre de este blog y de la Gerencia de la cual soy responsable en C&S informática s.a.

Tom DeMarco, Ingeniería de Software y Control de Proyectos (Marcio Degiovannini)

agosto 13, 2009

Hace unos días, Tom DeMarco escribió un artículo sobre métricas, control de proyectos e ingeniería de software en general donde podemos encontrar frases que claramente contradicen su postura de hace unas décadas, entre las que se encuentran las siguientes:

· Todos comprendemos que las métricas de software cuestan dinero y tiempo, y que deben ser usadas con moderación.
· El desarrollo de software es inherentemente diferente de las ciencias naturales tales como la física, por lo que sus métricas son mucho menos precisas para capturar lo que deben describir.
· La frase «No puedes controlar lo que no puedes medir» lleva implícita que el control es un aspecto importante, quizás el más importante, de cualquier proyecto de software. Pero no lo es.
· Muchos proyectos se han realizado sin demasiado control pero han generado productos maravillosos tales como Google Earth o la Wikipedia.
· Esto nos lleva a la desagradable conclusión que el control estricto es algo que importa mucho en proyectos relativamente inútiles y mucho menos en proyectos útiles.
· ¿Estoy diciendo que está bien ejecutar proyecto sin control o con un control relativamente menor? Casi. Estoy sugiriendo que deberíamos seleccionar primero a los proyectos cuyo control preciso no importe demasiado.
· En los últimos 40 años nos hemos torturado por nuestra ineptitud en acabar proyectos a tiempo y con el presupuesto previsto. Pero como sugerí antes, no debería haber sido el objetivo supremo.
· El objetivo más importante es la transformación, crear software que cambie el mundo, o que transforme una empresa, o la forma en que hace negocios.
· El desarrollo de software es y será siempre algo experimental. La construcción real de software no es necesariamente experimental, pero sí lo es su concepción. Allí deberíamos enfocar nuestros esfuerzos. Allí es donde deberíamos haberlo hecho siempre.

Está claro que es mucho más fácil criticar un artículo que hacer uno nuevo pero dada la trascendencia que está teniendo este escrito en particular voy a expresar mi opinión.

Creo que DeMarco sabe venderse. Creo que cuando era más joven se supo posicionar como gurú de la Ingeniería de Software y el control de proyectos y creo que ahora está en la edad en que puede decir que parte de lo que dijo está mal e igual quedar bien porque su posición coincide con la de muchos gurúes actuales. Sin embargo, me parece que el artículo está lleno de frases impactantes pero que  no salen de un análisis profundo. Me voy a explayar.

Que las métricas cuestan dinero, en cierto sentido, es cierto. También es cierto que ahora es mucho más fácil llevar algunas métricas que hace unos años, dado que ahora utilizamos herramientas que las generan de manera automática. Podríamos decir que en el presente es mucho menos costoso tener la misma información que hace unas décadas DeMarco consideraba importante.

Que el desarrollo de software es inherentemente diferente de las ciencias naturales como la física también es verdad. Lo que no se termina de entender es por qué nos comparamos con las Físicas y no con las Ingenierías. El diseño de una planta de gas en situaciones extremas también es único y con resultados impredecibles, la construcción de una torre en Dubai, algún nuevo prototipo de avión, un nuevo celular, etc. también son inherentemente diferentes de las ciencias naturales y sin embargo cuentan con presupuestos, métricas de avance, de defectos, etc. O sea, es una verdad, pero que no aplica a la conclusión a la que llega.

Donde puedo estar más de acuerdo es cuando hablamos del control de proyectos de software aunque más adelante voy a definir mi posición. En este caso, en mi opinión también escribe algo cierto: “El control no es lo más importante de un proyecto de software” pero cuando toma ejemplos, elije dos proyectos en los que las desviaciones admitidas eran del orden de entre 100% y 1000% ya que o el objetivo era otro, o los resultados esperados eran mucho mayores a una desviación de 500% en costos. Ambos proyectos (Wikipedia, que surge de NuPedia) y GoogleEarth fueron proyectos en los que hubo inversores poniendo su dinero durante años sin ver resultados. Se puede buscar en Internet acerca de la historia de NuPedia, antecesora de Wikipedia y a Jimi Wales explicando que después de haber gastado 250.000 dólares solo había logrado 16 artículos en su nueva Enciclopedia. Lo mismo pasó con GoogleEarth, anteriormente en manos de KeyHole con inversores como Sony y otros Fondos de Capital. En este tipo de proyectos, se pueden asumir pérdidas durante años hasta lograr un producto que puede romper con el mercado o también se puede perder todo lo invertido. Se trata de proyectos de inversión de alto riesgo y determinan proyectos de desarrollo que claramente son poco comunes. Al menos, en mi entorno, la mayoría de los proyectos son internos o externos (para un cliente). Los proyectos externos tienen presupuestos o se crean luego de llamados a licitación. Es decir, costo fijo, tiempo fijo, alcance fijo más menos alguna que otra variación. Es claro que si no contamos con un mínimo de control en estos proyectos, la empresa no sabe si está siendo rentable o no. Tampoco sabe si el producto que es desarrollado le puede traer problemas a futuro por alguna cuestión de calidad. No poner un mínimo énfasis en el control de estos proyectos puede llevar a la empresa a la quiebra sin que esta sea consciente salvo por los números rojos que aparecen en sus balances. Por lo tanto, explicar que un proyecto de software no hace falta controlarlo mucho y poner como ejemplo dos casos muy poco relevantes no parece muy serio.

Por último, cuando dice: el desarrollo de software es y siempre será algo experimental vuelve a caer en las afirmaciones de las que en 40 años (en caso de estar vivo, claro) podría volver a arrepentirse. Es cierto que todavía estamos en una etapa inicial de la que llamamos ingeniería de software. Es cierto que no es fácil gestionar proyectos de software por sus características intrínsecas (intangibilidad, maleabilidad, etc.) pero cada ingeniería tiene sus problemas particulares y cada una supo evolucionar a lo que son ahora. Seguramente la ingeniería de Software con el tiempo encontrará su camino.

Ahora bien, aparte de criticar un poco a DeMarco (que era lo más fácil), vamos a retomar la frase:
«No puedes controlar lo que no puedes medir” lleva implícita que el control es un aspecto importante, quizás el más importante, de cualquier proyecto de software. Pero no lo es.
En cierta medida, explicaba arriba, estoy de acuerdo con esta afirmación. Un buen proyecto de software con mucho control pero malos programadores lleva al fracaso inevitablemente (lo he vivido personalmente y por experiencias de terceros). Por el contrario, un buen equipo de programadores (pero de los buenos) puede terminar el proyecto más complejo haciendo que ningún indicador tenga sentido ya que la diferencia de calidad del producto y la eficiencia del trabajo de este tipo de gente multiplica por 10 o por 100 la de un equipo estándar. Este tipo de gente piensa las cosas de manera diferente, tiene la calidad incorporada como método de trabajo y la capacidad de generar soluciones a las distintas situaciones hace que el esfuerzo estimado pueda acortarse en órdenes de magnitud. Estuve dentro equipos de este estilo y lo que se puede lograr en un ambiente hiperproductivo difícilmente se logre en una empresa de desarrollo común y corriente.  El problema es que la gente que tiene estas cualidades escasea y el mercado está lleno de trabajadores “estándar” que requieren definiciones, procesos, un área de calidad que verifique sus entregables, y un PM que le indique qué es lo que tiene que hacer. De a poco, estimo yo, la industria irá decantando por rendimiento aquellos profesionales que hacen la diferencia y los estándares de productividad, calidad, base teórica requerida para un puesto irán aumentando. Fue pasando durante estas décadas y seguirá pasando en el futuro.

En síntesis, medir y controlar importa, importa más en las empresas con gente estándar, importa más en las empresas con proyectos estándar y, siempre que estemos dentro de una empresa, algo vamos a tener que medir, pero el control no es condición necesaria ni menos suficiente para lograr un proyecto exitoso. No es algo fundamental como tener un buen equipo de programadores, como tener gente que sepa interpretar lo que se pide y transformarlo de manera natural en la solución que el cliente necesita. Por eso cuando veo gente que se preocupa por aumentar el control o aumentar los procesos, creo que está perdiendo el foco, cuando lo que debería buscar es cómo hacer para encontrar y hacer rendir a los verdaderos profesionales del software.

Debate sobre la Ingeniería de Software (Carlos Fontela y Marcio Degiovannini)

julio 27, 2009

En unos días estaremos publicando en este blog algunas opiniones sobre el artículo de Tom De Marco por los 40 años de la Ingeniería de Software y algunas derivaciones que tuvo el mismo.

Para que no se aburran mientras, y para que vayan entrando en tema, les sugiero leer:

– El artículo de De Marco.

– La opinión de mi colega Sergio Villagra

– La apasionada opinión de Ricardo Galli

– Los comentarios en Navegapolis

Negociación (Cristian Pascuzzi)

julio 13, 2009

Todos los días nos encontramos en situaciones en las que estamos negociando. Bien o mal, por motivos reales o irreales (existen, por ejemplo rumores), por causas de valor o no, nos guste o no, pero siempre negociando.

Pero ¿qué es negociar?

Podemos decir qué no es negociar: Tomar decisiones de manera unilateral, cuando hay más de una persona o parte en conflicto. Para nuestra historia de dictaduras, sabemos quiénes no negociaron y quiénes no fueron invitados a la mesa de negociación.
La negociación es el método por el cual dos o más personas buscan ponerse de acuerdo sobre un tema en común. Una familia en el momento de decidir a dónde ir de vacaciones está negociando: son más de una persona (sino no habría con quien negociar), cada uno tiene intereses o ideas diferentes, pero un fin común (vacaciones todas juntas y en armonía).

¿Qué se necesita para negociar?

Hay varios puntos que se deben tener en cuenta al negociar, y algunos son:
– Buscar un objetivo común
– No mantener una posición
– No mezclar lo personal en la negociación
– Tener actitud positiva
– Tener una actitud ganar-ganar, buscar beneficios mutuos
– Ser creativo y buscar o prever opciones
– Ser objetivo, no subjetivo, evitar prejuzgar o si no se puede, manifestarlo
– Hacer solo críticas constructivas: las críticas no deben destruir al otro o su propuesta
– Buscar mejorar la relación o, por lo menos, no dañarla
– Pensar, no reaccionar
– No buscar culpables
Muchos de estos ítems son mencionados para trabajar en equipo eficientemente, para reuniones eficaces, y también para negociar.

El objetivo de la negociación

Este punto es importante, porque estamos negociando a causa de la existencia de un objetivo. Obvio, ¿no? Pero ¿siempre lo tenemos en cuenta? ¿Siempre nos ponemos a revisar el objetivo antes de negociar? ¿Tenemos claro el fin de la negociación?
El objetivo de toda negociación debería ser básicamente, el de mejorar la relación con las otras partes, logrando un obtener un beneficio mutuo.

Los Intereses

Los intereses de cada parte pueden ser idénticos, parecidos o totalmente diferentes y opuestos. Lo mencionamos el ejemplo de la familia que se va de vacaciones. Un caso de intereses opuestos podría ser el del vendedor y el comprador: el primero vender su producto obteniendo la mayor ganancia, mientras que el segundo quiere comprar pagando lo menos posible.
En este caso el objetivo común es lograr realizar la operación de una manera que se beneficien ambas partes, manteniendo o mejorando la relación. Parece sencillo determinar el objetivo en este caso, ¿no? ¿Lo hacemos a menudo?
Para poder prepararnos a negociar, es importante tener claros los intereses que nos mueven. Para esto hay preguntas claves:
– ¿Cuáles son nuestros intereses?
– ¿Tenemos una justificación de nuestros intereses, obviamente racional y lógica?
– ¿Quiénes se benefician de lograr estos intereses?
– ¿Cuáles son los intereses de las otras personas que están afectadas indirectamente?
– ¿Podemos imaginarnos los intereses de la otra parte?
Muchas veces no nos ponemos a pensar antes de ir a una negociación. Unos minutos previos nos permitiría tener claros nuestros intereses, pensar en los de la otra parte, lograr una sinergia entre ambos, y así buscar una mejor negociación, quizá no el mejor resultado, eso dependerá de cómo negociemos.

Podemos pensar que a veces las tratativas con determinadas personas es difícil, pero debemos tener claro que hablar con esas personas que “nos parecen“ difíciles, no es imposible.
Revisar los intereses y otros puntos que veremos mas adelante, nos permitirán quitar lo personal de la negociación.

Por ejemplo:

¡Hablar con Juan es difícil, nunca escucha! ¿Es Juan el difícil o no puedo comunicarle mi idea? ¿Puedo expresarme de otra manera? ¿Estoy hablando con él de la idea o mientras hablo cargo mi mensaje con mis prejuicios, quizá inconscientemente? ¿Mi mensaje es directo o me voy por las ramas, y logro que Juan se pierda?

En este ejemplo, sólo busqué preguntas que me hagan pensar si la razón de que me sea difícil hablar con Juan no sea parte de mi forma de encarar la negociación.
Para resolver un tema como este, además de saber que “debemos” encarar la charla con Juan, también tenemos que revisar nuestros intereses y separar lo personal del objetivo.

En Resumen:

  1. Siempre estamos negociando “algo” con “otros”
  2. El objetivo de la negociación debe ser lograr un acuerdo que beneficie a todas las partes, y mantener o mejorar la relación
  3. Antes de negociar, analizar quienes están involucrados directa e indirectamente, y los intereses de todos

Diseño orientado a objetos: refactorizando métodos (Carlos Fontela)

junio 30, 2009

Los métodos representan el comportamiento de los objetos. Sin embargo, este comportamiento debe estar correctamente modularizado, respetando los principios de acoplamiento y cohesión que analicé en mi artículo respectivo.

Los métodos suelen surgir con bastante claridad con las responsabilidades de una clase. Sin embargo, hay situaciones de refactorización (ver el ejercicio) en las cuales surgen más métodos, que tal vez no estuvieran en el contrato de la clase.

Las razones típicas que nos llevan a introducir nuevos métodos son:
– Reducir la longitud de un método
– Reducir la complejidad de un método, recordando que cada módulo debe tener un propósito simple (cohesión)
– Hacer legible un pedazo de código que se complica mucho entender
– Simplificar tests booleanos sacándolos fuera
– Evitar duplicación de código

Visibilidad

Antes de seguir, una reflexión: si estamos creando métodos que no son parte del contrato de la clase, sino sólo por refactorizaciones que aumenten la calidad del diseño, los nuevos métodos deben ser lo más privados que sea posible. En la medida que no lo hagamos así, vamos a provocar una posible dependencia desde otras clases, que nos obligarán a mantener el método en cuestión en versiones futuras de la clase, testearlo cada vez, etc.

Reduciendo la longitud y aumentando la cohesión

Reducir la longitud de un método es importante por dos razones: para mejorar la legibilidad (un método muy largo suele ser difícil de leer) y mejorar la cohesión (si bien no es siempre cierto, un método largo tiende a no ser cohesivo), con lo cual atacamos las dos primeras razones.
A menudo nos encontramos con métodos que tienen comentarios que marcan separaciones semánticas. Por ejemplo:

void m ( ) {
// hago X:
… código …
// hago Y:
… código …
// listo los resultados:
… código
}

Claramente, este método del ejemplo se podría (y debería) dividir en tres métodos.
Otros indicios menos obvios, pero muy efectivos, para separar un método en partes es analizar:
– El ámbito de uso de una variable local o parámetro: si una variable se usa sólo en parte de un método, es una buena razón para pensar que esa parte del método podría convertirse en un método separado y cohesivo.
– Ciclos: si parte de un método es un ciclo iterativo, es factible que también esa parte pueda separarse en un método cohesivo.
– Estructuras anidadas: los ciclos o condicionales anidados se pueden extraer como nuevos métodos, mejorando la legibilidad y la cohesión.

Separando tests booleanos

En cuanto a los test booleanos. Supongamos que en una clase Fecha tenemos un método para validar fechas, que contiene el siguiente fragmento:

if  ( (anio % 400 == 0) || ( (anio % 4 == 0) && (anio % 100 != 0) ) )
diasMes = 29;
else diasMes = 28;

¿No es cierto que sería mejor separar un método booleano?, así:

private boolean bisiesto() {
if ( (anio % 400 == 0) || ( (anio % 4 == 0) && (anio % 100 != 0) ) )
return true;
else return false;
}

Y luego dejar nuestro fragmento así:

if ( bisiesto ( )
diasMes = 29;
else diasMes = 28;

Eliminando código duplicado

Lo de la duplicación de código lo dejé para el final, porque suele ser la primera razón citada por cualquiera al que le preguntamos por la extracción de métodos. Sin embargo, no todo es tan sencillo.

Si tenemos código repetido dentro de un mismo método o dentro de varios métodos de una misma clase, debemos crear un método nuevo, privado, con ese código repetido, y colocar las invocaciones al método donde antes había código repetido.

Si, en cambio, el código repetido está en clases distintas, tenemos dos situaciones posibles. Si el código en cuestión está en dos clases “hermanas” (hijas de una misma clase base), podemos crear un método, moverlo a la clase madre, y hacerlo protegido.

Si el código repetido está en dos clases que no están en la misma jerarquía de herencia, debemos crear una nueva clase, colocar allí el método, y utilizarlo por delegación o herencia, lo que sea mejor desde el punto de vista del diseño.

Modularidad, cohesión y acoplamiento (Carlos Fontela)

junio 23, 2009

La idea de que un buen diseño debe estar basado en una alta cohesión y un bajo acoplamiento viene de los años 1980. Fue uno de los primeros principios proclamados del diseño estructurado, y pasó luego a la orientación a objetos.

¿Y a qué llamamos cohesión y acoplamiento? Tiene que ver con la modularidad, así que empecemos por ella.

Modularidad

El aporte más importante que hizo el diseño estructurado fue la idea de que, para resolver un problema complejo de desarrollo de software, conviene separarlo en partes más pequeñas, que se puedan diseñar, desarrollar, probar y modificar, de manera sencilla y lo más independientemente posible del resto de la aplicación.
Esas partes, cuando se quiere usar un nombre genérico, habitualmente se denominan módulos. De allí que otro nombre para la programación estructurada, luego caído en desuso, fue “programación modular”.
El diseño estructurado, al plantear la separación del sistema en módulos, se basó en las propias funciones del sistema. Esto es, los módulos de la programación estructurada serían los procedimientos y funciones. Por lo tanto, al modularizar, lo que hacíamos era tomar nuestra solución del problema, y separarla en partes. Deténgase aquí y asegúrese de que entiende lo que le digo: en programación estructurada modularizamos la solución, el “cómo” del desarrollo.
En el diseño orientado a objetos, en cambio, la modularización esencial se da a nivel de clases, que no son funciones del sistema, sino (al menos en una primera aproximación) entidades del dominio del problema. Por lo tanto (y asegúrese de entender también esto), en el análisis y diseño orientados a objetos, no modularizamos la solución, sino primero el problema (en el análisis) y luego, partiendo de esas clases conceptuales, del dominio del problema, modularizamos la solución (diseño). Bertrand Meyer tiene una frase bastante acertada al hablar de cómo obtener los módulos de la orientación a objetos: “no pregunte qué hace el sistema, sino a quién se lo hace”.
Por supuesto, la orientación a objetos también tiene módulos funcionales, que serían los métodos u operaciones de las clases, pero estos tienen una importancia menor respecto del módulo por excelencia, que es la clase.
Finalmente, en el diseño orientado a objetos, suele aparecer otro tipo de módulo más, el paquete, de escasa relevancia semántica, pero importante para agrupar clases en el diseño de aplicaciones medianas.

Cohesión

La cohesión tiene que ver con que cada módulo del sistema se refiera a un único proceso o entidad. A mayor cohesión, mejor: el módulo en cuestión será más sencillo de diseñar, programar, probar y mantener.
En el diseño estructurado, se logra alta cohesión cuando cada módulo (función o procedimiento) realiza una única tarea trabajando sobre una sola estructura de datos. Un test que se suele hacer a los módulos funcionales para ver si son cohesivos es analizar que puedan describirse con una oración simple, con un solo verbo activo. Si hay más de un verbo activo en la descripción del procedimiento o función, deberíamos analizar su partición en más de un módulo, y volver a hacer el test.
En el diseño orientado a objetos las cosas son más complejas. Como dijimos, hay tres tipos de módulos: clases, métodos y paquetes. Con los métodos, podemos adoptar las mismas definiciones y recetas que para los procedimientos y funciones del diseño estructurado. ¿Qué ocurriría con los otros dos?
Las clases tendrán alta cohesión cuando se refieran a una única entidad. Podemos garantizar una fuerte cohesión disminuyendo al mínimo las responsabilidades de una clase: si una clase tiene muchas responsabilidades probablemente haya que dividirla en dos o más. Y el test a aplicar sería ver si podemos describir a la clase con una oración simple que tenga un único sustantivo en el sujeto. Si la clase estuviera representando alguna operación (por la aplicación de algún patrón de diseño, por ejemplo), también habría que tratar de “sustantivarla” y aplicarle la prueba para ver si es cohesiva. Una clase con alta cohesión suele cumplir el principio de única responsabilidad.
En los paquetes no es usual analizar cohesión. Sin embargo, nadie nos impide aplicarle los mismos tests que a las clases. Sin embargo, lo crucial en los paquetes es el acoplamiento, que vamos a ver ahora.

Acoplamiento

El acoplamiento mide el grado de relacionamiento de un módulo con los demás. A menor acoplamiento, mejor: el módulo en cuestión será más sencillo de diseñar, programar, probar y mantener.
En el diseño estructurado, se logra bajo acoplamiento reduciendo las interacciones entre procedimientos y funciones, reduciendo la cantidad y complejidad de los parámetros y disminuyendo al mínimo los parámetros por referencia y los efectos colaterales.
De nuevo, el diseño orientado a objetos nos complica las cosas con sus tres tipos de módulos. A los métodos, como pasó con la cohesión, podemos analizarlos con los mismos criterios que a los módulos del diseño estructurado.
Una clase, en cambio, tendrá bajo acoplamiento cuando tenga la menor dependencia posible de otras clases. Esta dependencia significa que – si bien puede haber muchas clases que dependen de una – debería haber pocas dependencias hacia otras clases desde una sola. Las dependencias que importan son, de mayor a menor: generalización/herencia, composición, asociación y dependencia débil. Para visualizar estas cuestiones, los diagramas de clases son herramientas fundamentales.
¿Y los paquetes? Un paquete debe cumplir con estos mismos requisitos, en el sentido de que debe tener vinculaciones mínimas con otros paquetes. Decimos que hay dependencia entre paquetes cuando hay clases de un paquete que dependen de clases de otro paquete, sea por herencia, asociación o simple dependencia débil. En este caso, podemos ayudarnos con un diagrama de paquetes, que debido a que nos muestra dependencias entre conjuntos de clases, nos sirve para eliminar problemas de acoplamiento.

Legibilidad de código (Carlos Fontela)

junio 19, 2009

Hace tiempo que prometí un artículo sobre calidad de código (ver mi primer artículo en este blog). También escribí uno sobre comentarios en el código y otro sobre estilos de codificación. Con este sobre legibilidad, espero estar cubriendo lo que faltaba del tema. Más adelante encararé cuestiones de diseño, tales como diseño de clases, paquetes y métodos.

Cuando escribimos algo, sea un libro, un informe, una tesis o un artículo en un blog, solemos trabajar con ayudas para la lectura, como por ejemplo:
– Títulos de distinto nivel
– Sangrías
– Signos de puntuación
– Tipografías especiales

Todo esto, si bien no cambia el contenido de fondo, facilita mucho su lectura. Piense el lector qué pasaría si tuviera que leer un libro sin capítulos, tipografía y signos de puntuación.
Ahora bien, el código fuente se escribe para que lo lea un compilador. ¿Para qué hacerlo legible?

Precisamente aquí está la mayor confusión respecto del propósito del código fuente. Si bien es cierto que la razón primaria por la que generamos código es para que un traductor lo lea y genere código ejecutable, lo cierto es que, debido al debugging, al mantenimiento y a las refactorizaciones, el código fuente se lee mucho más por humanos que por máquinas. Y es más: se lee muchas más veces de las que se lo escribe.
Por lo tanto, si hacemos legible el texto, ¿por qué no hacer legible el código?

Hay unos cuantos factores que afectan la legibilidad.

Layout y espaciado

Manteniendo la analogía con el texto, deberíamos empezar por temas de organización del texto.

Hay muchos programadores que escriben código como si los multaran por cada espacio en blanco innecesario en sus programas, y eso es realmente lamentable, porque los espacios y líneas en blanco son los recursos que más legibilidad agregan al menor costo: se logra solamente presionando las dos teclas más grandes de cualquier teclado.

Por ejemplo, ¿por qué escribir:

x=f(a,b,c);

si podemos escribir:

x = f (a, b, c);   ?

¿Y por qué escribir un método debajo de otro, si queda más legible dejando una línea en blanco entre ellos?

Las sangrías son otro recurso interesante para mostrar anidamiento de bloques, instrucciones de selección o iteración.

Finalmente, los paréntesis, llaves, bloques “begin … end” y cuestiones similares, facilitan la lectura aun cuando no sean necesarios. Es realmente contraproducente pretender que el lector del código recuerde de memoria todas las reglas de precedencia, sobre todo si el escritor no las recuerda: debemos suponer que el lector es del mismo nivel que el escritor; por lo tanto, si en algún momento necesita ir a la documentación del lenguaje para chequear reglas de precedencia, no lo haga: use paréntesis.

Comentarios

Los comentarios hacen mucho por la legibilidad del código, a veces… Pero ese tema lo traté en otro artículo.

Números mágicos y hard code

El hardcodeo y los “números mágicos” (números que figuran literalmente en el código) traen muchos problemas que exceden el de la legibilidad. Sin embargo, también representan un problema de legibilidad.
Por ejemplo, en las líneas que siguen, en Java, hay uno de estos casos por línea:

Collection c = new ArrayList(14);        // 14 clientes
for (int i = 1; i <= 12; i++) …                   // meses del año
int[ ] horas = new int [5];                        // de lunes a viernes
if (usuario == “Juan Gómez”) …

Por supuesto, hay algunos “números mágicos” que están bien usados, como muchos 0, 1, null o nil, strings vacíos, etc.

Nombres

Cuando usamos nombres de variables, funciones, métodos, clases, paquetes, etc., una buena elección de los mismos aumenta la legibilidad.
La idea es elegir nombres que describan lo que se está nombrando, sin abreviaturas en la medida de lo posible (ya pasó la época de los lenguajes que permitían hasta 6 u 8 caracteres) y usando mayúsculas y minúsculas cuando sea apropiado (esto depende de que el lenguaje haga la distinción).
Por ejemplo, llamar lineasPorPagina a una variable es mejor que llamarla lpp. Eso no quita que algunas veces usemos abreviaturas suficientemente establecidas: min, max, temp, aux, long, num, i, j y k para índices.

Si una variable tiene una magnitud, habría que indicarla. Por ejemplo, DistanciaEnMetros es mejor que Distancia.

Un nombre de función o método debería representar lo que hace ese método. Por ejemplo, un método que elimina un empleado de una lista, se debería llamar eliminarEmpleado, y se sobreentiende que para eliminarlo hay que encontrarlo antes. Son malos sucedáneos: buscarEmpleado, procesarEmpleado , buscarEmpleadoEliminar, buscEmpEl, bee, buscarEliminar, findEmpleadoDelete, por distintas razones: “procesar” es un verbo que no dice nada, “findEmpleadoDelete” es una mezcla de idiomas horrenda, y así sucesivamente.
Además, debería describir lo más posible la intención y no cómo lo hace. Por ejemplo, en Java existe un método Arrays.binarySearch() que no es un buen ejemplo; ¿por qué no busquedaRapida?

Si usamos conceptos que representan antónimos, es mejor hacerlo en forma consistente: si tenemos una variable que se llama mayor, su contraria debiera llamarse menor, y no mínimo, por ejemplo.

Una cuestión a evitar es el uso de un mismo identificador con más de un objetivo.

Si trabajamos con nombres en castellano, que todos estén en castellano. Si es en inglés, que todos estén en inglés. Si en guaraní… bueno, se entiende, ¿no?

Las variables booleanas debieran expresar condiciones lógicas (que puedan ser verdaderas o falsas) lo más afirmativas posibles. Por ejemplo encontrado, terminado y error, son buenos nombres. En cambio, sexo, noEncontrado, no lo son.

Otra buena práctica es hablar en términos del problema y no de la solución. Por ejemplo, podemos nombrar a una estructura listaEmpleados o simplemente empleados, pero no sería tan bueno ponerle vectorEmpleados.