Muéstrame una solución por la cual los desarrolladores no discutirán, y te prepararé una cerveza



Hay dos campamentos irreconciliables. Algunos dicen: debe describir de manera precisa y competente sus compromisos. Cada compromiso es un trabajo completo y significativo. Si no puede hacer una descripción clara y simple de una confirmación, entonces tiene las confirmaciones incorrectas.

Otros consideran que se compromete a su gusto, esto es parte de su flujo de trabajo personal. Pero el conjunto de solicitudes se describe en detalle: qué se hace, cómo se hace, por qué se hace. Según lo probado, qué problema resuelve, a qué debe prestar atención.

Soy un firme defensor del segundo enfoque: es inconveniente para mí dividir mi trabajo en micro piezas. Tomo una pequeña tarea y me apresuro al azar alrededor de la base del código, experimentando y haciendo cambios en el orden en que resulta. Si pudiera hacer clic en un botón y mi trabajo se reestructurara en buenos compromisos, haría clic en él. Pero no hay un botón, pero no quiero romperme. Al mismo tiempo, alcancé cierta habilidad para describir el conjunto de solicitudes. Descubrí el código en Microsoft (a través de personal, no cuenta), y allí obtuve los estándares más altos para procesar un grupo de solicitudes. Con los años, acabo de desarrollar esta práctica. Por lo general, me las arreglé para convencer al equipo de que usara tal enfoque.

Pero en el último trabajo, obtuve devlid, un firme defensor de los compromisos detallados. Oh, discutimos por mucho tiempo. Mi posición subordinada jugó un papel, el hábito Sovkovsky de estar de acuerdo con lo principal no es fácil de callar. No era tan categórico como de costumbre, y me pusieron en ambas cuchillas. Apilados, pero no convencidos.



Si de repente los desarrolladores se unen en un solo gobierno, establecen reglas y comienzan a proclamar ciertos enfoques prohibidos, una guerra civil comenzará al día siguiente. Todo porque en el mundo de los desarrolladores existe una creencia en lo objetivo y medible. En la verdad independiente de la percepción humana. Y si todo esto existe, significa que uno de los disputadores está claramente objetivamente equivocado.

Mi primera disputa laboral seria ocurrió bastante temprano. Todavía era un verde de junio, que piensa que es un buen medio, y de hecho un desarrollador genial e inteligente. Un desarrollador real de mi equipo lanzó PR. Teníamos la práctica de que todos los miembros del equipo hicieran un código de revisión. Abrí el código y casi de inmediato vi el problema. Un hombre escribió una prueba para alguna función que tomó un número. Entonces él la alimentó con 0, 1000 y al azar (0, 1000).

En ese momento, sentí muy profundamente que otros compañeros de equipo me veían como un estúpido recién llegado. Y esperó un momento para untarlos con su visión. Tuve suerte, ¡al azar en las pruebas!

Realmente no entendía la teoría de las pruebas unitarias, pero leí un par de libros y recordé firmemente: la misma prueba unitaria en la misma base de código debería dar el mismo resultado. Pasé aproximadamente una hora pensando en el comentario para que no pareciera tóxico, pero dejé en claro que solo el mono, al que se le enseñó a contar ayer, podía pensar en tal decisión. Al final, por supuesto, se veía extremadamente estúpido, como todo lo que los internos de ayer que se imaginaban a sí mismos como desarrolladores se exprimían.

Al día siguiente, me di cuenta de que había abierto un portal al infierno. Había cien más bajo mi comentario. Durante el resto del día, en lugar de un equipo de seis desarrolladores, fuimos un equipo de seis monos. Discutimos en inglés en PR, aunque todos eran rusos. Cuando la cantidad de palabras no era suficiente, llamaron. Se lanzaron el uno al otro con enlaces, citas de libros, estudios, dirigidos al individuo. Un colega respondió uno de mis comentarios con una captura de pantalla de un diccionario de inglés para ridiculizar mi inglés. Pero no hemos llegado a un compromiso.

La pregunta fue complicada. Por un lado, todos no sabíamos de qué era responsable este parámetro, nuestra función lo pasó al otro, desde una red interna a la que no había muelles. Por lo tanto, teóricamente, la función podría caer en cualquier valor, lo que significa que la prueba podría atraparlo, y luego podríamos anticipar el error en el producto. Por otro lado, ya teníamos el problema de construir versiones al azar. Debido a esto, si el proyecto se estaba construyendo localmente y se estaba bloqueando, pedimos estúpidamente una nueva construcción y no miramos ningún registro. Después de todo, la construcción tomó cuatro horas, y nadie miraría la solicitud de extracción hasta que pasara CI.

Además, los automóviles a menudo estaban ocupados: era mucho más pragmático pedir una construcción mientras aún se ordenaba que buscar un "problema" potencial (que casi siempre resultó ser un tiempo de espera excedido en la prueba de integración). Esto significaba que incluso si la prueba aleatoria fallaba, no prestaríamos atención.

Todo se resolvió en el siguiente acuerdo. Llamamos a todo el equipo y durante media hora seguimos discutiendo, en ruso. No nos dimos cuenta de cómo nuestro gerente de desarrollo estadounidense se unió a la llamada, y continuamos aplastándonos mutuamente con argumentos en tonos elevados. Hasta que escucharon "Hola chicos, no es absolutamente importante. Simplemente combínalo como está ". No sé cuánto tiempo nos escuchó antes, y como entendió lo que estábamos hablando, pero dejamos de discutir abruptamente. Se congelaron y lo olvidaron. Separados en paz, pero no convencidos.



Desde entonces, he adquirido experiencia para comprender, pero no me importan en absoluto los casos aleatorios, de discapacidad o límite. Esta maldita prueba unitaria interferirá, la reescribiremos. Todo es mejor que discutir todo el día. Pero si imagina que estoy en un mundo ideal, donde siempre es necesario tomar decisiones más correctas, no sé qué hacer. Congelamos una prueba con aleatoriedad, y ahora entiendo esto como una opción a favor de la confiabilidad, pero en detrimento de la conveniencia del desarrollo.

El otro día me enfrenté a un dilema similar. Jugué con el nuevo código de tiempo y escribí algo que se puede usar así

// LessThan<T>  MoreThan<T> -    ,   . 
// ,        

//      , 
//   ,   100
const consumeNumberLessThan100 = (v: LessThan<100>) => 
    doWork(v);

//     ,   100
const consumeNumberMoreThan100 = (v: MoreThan<100>) => doWork(v);

const sample = (x: number) => {

    //     check  , 
    //   x  100
    //  "<"  ">"  , 
    //     -   
    const lessThan100 = check(x,'<', 100);

    if (lessThan100) {
        //     -  
        //       
        //    ,  lessThan100  - LessThan<100>
        //    
        consumeNumberLessThan100(lessThan100); 

        //   -  ,   , 
        //  ,   > 100.
        consumeNumberMoreThan100(lessThan100);
    }

    const moreThan100 = check(x, '>', 100);

    if (moreThan100) {
        consumeNumberMoreThan100(moreThan100); // 
        consumeNumberLessThan100(moreThan100); // 
    }

    consumeNumberLessThan100(x); // 
    consumeNumberMoreThan100(x); // 
}

Pensé, maldita sea, esto es muy bueno, por lo que puedes detectar una gran cantidad de errores en una etapa anterior. El diseño de mis herramientas de restricción de valor es terrible, pero por ahora es solo un concepto. En el futuro, puede expandirlo fácilmente, crear un DSL potente y formular condiciones realmente complejas para la coincidencia de parámetros garantizada en la etapa de compilación. Descubrí cómo puede aumentar la fiabilidad de cualquier base de código, animarme y enviar un fragmento a varios desarrolladores familiares.

Las opiniones se dividieron nuevamente, y no en mi dirección. La nueva complicación, la sobre ingeniería, no es compatible, todo el mundo bloqueará tu guardia con la ayuda de un elenco para eni. No se puede leer. Bueno como experimento es malo en el presente proyecto. Se extraen ejemplos de uso del dedo. Póngase manos a la obra, Phil.

Los defensores del enfoque dijeron, sí, muy confiable. Cuanto antes descubras un error, mejor. Además, si escribe una función que funciona con un número limitado, todavía tiene que escribir un cheque, pero solo funcionará en tiempo de ejecución y aumentará la cantidad de código en el cuerpo de la función.

Ahora soy un poco más inteligente que antes y aprendí a escuchar los argumentos. Me imaginé escribiendo una función protegida por mi tipo en un proyecto real. Como todos los que lo usan, me preguntan qué demonios es esto. Cómo, después de manipular el chip, comienzan a recubrir la base del código con un DSL personalizado de aspecto podrido. A medida que verificamos trescientas veces los valores que de hecho nunca excederán lo permitido. El enfoque es realmente terrible de usar, algunos problemas pueden resolverse o suavizarse, pero por ejemplo, tal caso

consumeNumberLessThan100(90);

No alisar de ninguna manera. Tendré que demostrarle al compilador que 90 es menor que 100. Proporcionaré cualquier actividad y resultará

consumeNumberLessThan100(assert(90, '<', 100)); 

No se ve muy bien. Todos los argumentos en contra están disponibles, pero no contradicen los argumentos a favor. Resulta un dilema: facilidad de desarrollo o fiabilidad. Aquí caemos en la trampa, comenzamos a pensar que necesitamos calcular qué tipo de conveniencia hay y qué tipo de confiabilidad hay. Pero la conveniencia y la confiabilidad en el desarrollo son cosas muy, muy complejas. Consisten en miles de parámetros.

Por ejemplo, la conveniencia es cuando el IDE compila el código para usted en un par de caracteres ingresados, cuando el código es fácil de leer, cuando puede cambiar la funcionalidad del método sin mirar el documento. Cuando el compilador no se carga con análisis estático, la compilación es rápida, el editor de texto representa caracteres al instante. Pero cuando el compilador detecta y resalta el error por usted, esto también es una conveniencia. Y también es fiabilidad. Que a su vez se ensambla a partir de una gran cantidad de cosas completamente diferentes.

Tiene que sentarse y calcular cuánto durará el proyecto, en qué dirección irá, cuántas veces en la historia de la humanidad alguien llamará a uno u otro método de su base de código, qué desarrolladores trabajarán aquí. Esta es una lista interminable de preguntas para la buena mitad de las cuales es imposible calcular la respuesta correcta. Solo adivina.



Una vez, un amigo me pidió que preparara preguntas técnicas para una entrevista con Andrei Breslav. Fui al muelle de Kotlin para encontrar puntos controvertidos en el diseño. Están allí, como en otros lugares, oscuridad. Pero lo que más me interesó fue el enfoque para manejar las excepciones. Un controlador de excepciones en Kotlin es una expresión, un enfoque completamente funcional y confiable. Eso es solo para manejar errores no es necesario. Lo que reduce toda la fiabilidad a cero. Y esto es interesante porque, justo en el banquillo, los desarrolladores no fueron demasiado flojos para explicar su elección: "hay estudios de que el manejo obligatorio de errores reduce en gran medida la productividad de los desarrolladores con una ligera disminución en los errores".

Estoy loco, ¿cómo puedes escribir código si no tienes idea de qué hacer cuando no funciona correctamente? Si no sabe cómo manejar la excepción, no la maneje, no es una solución al problema, sino dejar de lado. En algún momento, el código se caerá y un problema que siempre existe causará daños que podrían haberse anticipado.

Pero no es necesario un argumento lógico en contra, puede hacer solo estadísticas. Para los desarrolladores de kotlin, la investigación rompe la lógica porque tienen una filosofía. Su filosofía es el pragmatismo. Pragmatismo irrompible e irrompible, integrado constantemente en todas las características de este lenguaje de programación. Los idealistas que vieron los Haskels / Idris y los govnokoders que escriben el golang hacen exactamente lo mismo. La filosofía del compromiso razonable reina en la base del código F #. Y no hay sensación de que uno de ellos tenga razón, y el resto son tontos.

Todas estas personas son mucho más inteligentes y tienen más experiencia que las personas como yo, y parecen haber entendido por mucho tiempo que estás desarrollando una filosofía para ti, y luego simplemente la sigues. Porque la característica asesina de cualquier filosofía es resolver los dilemas.

Y así, la filosofía apareció en todo en TI, y la filosofía es exactamente lo contrario de la idea de una objetividad única y verdadera, porque la filosofía ofrece elegir la verdad subjetiva por ti mismo.

Cada uno de nosotros tiene nuestra propia filosofía: no se describe en una palabra, es un conjunto complejo de patrones para tomar decisiones. Y a menudo los cambiamos o ampliamos. En la práctica, resulta que tiene un proyecto con su propia filosofía, que está escrito en un lenguaje de programación con su propia filosofía, utilizando marcos y herramientas, cada uno de los cuales tiene su propia filosofía. Y los desarrolladores escriben este proyecto, cada uno con su propia filosofía. Y cada vez que necesite tomar algún tipo de decisión, solo una combinación de circunstancias afecta cuál se tomará. No es la gestión de la complejidad, ni la experiencia, ni el enfoque, ni el conocimiento, sino solo un accidente.

Y todos estos proyectos funcionan. La mayor parte de todas las tareas que los desarrolladores resuelven es la eliminación de las consecuencias de los errores de otros desarrolladores, pero los proyectos funcionan y resuelven los problemas de las personas. A las personas inteligentes se les ocurren prácticas y patrones arquitectónicos. Lenguajes de programación con control de tipo y contratos potentes: todo, si los desarrolladores estuvieran menos equivocados. Y todos los días abro el Edge Board en el trabajo, y veo que hay más errores que tareas. ¿Dónde está cada error, este es otro, la complejidad de gestión, desarrollador de basura.

La filosofía de desarrollo es simplemente la elección de la forma en que tienes en tus manos la final. Pero si no hay filosofía, sino solo lógica objetiva pura, cualquier argumento se reducirá al problema del creador omnipotente y la piedra insoportable.



He estado en desarrollo durante mucho tiempo y aprendí a hacer lo que dicen, incluso cuando estoy en desacuerdo fundamentalmente, y comencé a escribir confirmaciones súper detalladas. Mi devlid es un verdadero vampiro. Requiere no solo una descripción: quiere que escriba lo que hace cada archivo, por qué lo hace, cómo lo hace. ¿Qué problema confirma commit?

Agregué ocho confirmaciones extra detalladas, la misma descripción detallada del grupo de solicitudes, y me gustaron los kapets. Desde entonces, casi no trabajamos en este proyecto en particular, pero de vez en cuando voy a admirar estos compromisos. En serio, realmente genial. Y en todos los demás proyectos, excepto los personales, ahora aplico este enfoque.

Fue muy difícil para mí reconstruir el flujo de trabajo. Ha pasado un mes y medio, y todavía me cuesta escribir código como este. Pero parece que vale la pena.

O no. Honestamente no lo sé. Y no sé si definitivamente vale la pena, ¿me hace dos meses un completo idiota? Alrededor de diez personas caminan por algún lugar del mundo al que he aprendido a no hacer tales compromisos. ¿Son idiotas? No lo creo.



Mi podcast

All Articles