¿Cómo evitar que los cambios en una API interna rompan otros proyectos?

8

Tenemos como 20-30 módulos / soluciones independientes. Cada uno de estos tiene entre 7 y 10 proyectos con diferentes clases, componentes, etc. Todos estos se utilizan internamente en nuestra empresa.

Nuestro problema es que cuando hacemos un cambio en un módulo, debemos asegurarnos de actualizar todos los demás módulos que acceden a este código. Esto es difícil de saber porque está en diferentes bases de código.

¿Cómo podemos documentar dónde están todos los usos externos de una API? ¿O de lo contrario evitará que pequeños cambios rompan otros módulos?

Matemáticas
fuente
66
¿Cómo evitar que los cambios de API rompan el código? Umm ... no cambies la API. Realmente, ¿qué respuesta podría haber aparte de eso?
@ dan1111, ¿y si tenemos que hacer cambios en la API y no sabemos qué otros módulos vamos a romper, cómo puede evitarse esto? Cajero automático, obtengo el nombre del método y busco nuestros archivos de base de código en el disco duro, una forma no tan eficiente de hacerlo
Matemáticas
¿Qué tiene de malo buscar en su base de código las llamadas a métodos? Si no tiene documentación de qué usa qué, esta es una muy buena manera de hacerlo. Podría escribir fácilmente una pequeña secuencia de comandos que busque todo su código para todas las llamadas API e informes sobre esto.
1
¿Puede darnos más detalles? ¿Qué lenguaje de programación? ¿está bien la compatibilidad con el código fuente o necesita compatibilidad binaria? ¿"No cambia la firma de la API existente" sino que solo agrega nuevas firmas de método resuelve su problema? ¿Su idioma admite "parámetros predeterminados"
K3b

Respuestas:

13

La solución más simple, IMO, es tener un número decente de pruebas automatizadas para cada producto. Cuando actualiza una biblioteca, ejecuta el conjunto de pruebas para cada producto. Si las pruebas fallan, entonces sabrá qué productos deben actualizarse.

Bryan Oakley
fuente
5

Sugeriría que no intente documentar esto (al menos manualmente) ya que si requiere que las personas lo actualicen, requerirá un alto nivel de precisión para funcionar correctamente. Pero nunca obtendrá ese nivel de precisión porque agregar este tipo de documentación ... no sería divertido y nadie lo hará.

Algunas mejores opciones:

  • Tenga una secuencia de comandos que genere una lista utilizable de todas las llamadas a métodos en todos los demás módulos, según los criterios de búsqueda. Simple, crudo, pero probablemente efectivo.
  • Algún tipo de sistema de versiones en el que no rompa la compatibilidad con versiones anteriores, excepto en los números de versión principales. Entonces, si cambia el método foo de la versión 2.1 a la 2.2, todo el código que haga referencia a la versión 2.X seguirá funcionando. Cuando necesite romper esta compatibilidad, actualice el número de versión principal (en este caso, 3.0) y anuncie esto a todos los propietarios de proyectos responsables de otros módulos. Dependiendo de cómo funcione su proceso de lanzamiento, esto puede ser simple, o increíblemente complicado, de implementar.
  • Realice pruebas automatizadas o un proceso de compilación de CI en el que cada vez que inserte código, una compilación ejecuta pruebas de algún tipo. Esto le permitirá identificar dónde ocurren los problemas. Supongo que con lo que estás describiendo como el problema, no lo tienes ya implementado.
  • Algún tipo de sistema de documentación automática para todos sus módulos / repositorios

También puede considerar refactorizar lentamente las API para que no sean tan frágiles, pero espero que eso esté fuera del alcance de lo que razonablemente puede lograr si es un individuo y tiene más de 20 módulos de gran escala para trabajar.

Enderland
fuente
1
+1 para el control de versiones, más un enlace al
Control de
2

En primer lugar, una API con usos externos no debería cambiar.

Como mencionó @BryanOakley, el uso de pruebas unitarias automatizadas es muy importante y salva vidas en tales situaciones. Más allá de eso, algunas sugerencias que pueden (o no, según la situación) ayudarlo

  • Muchos lenguajes (como Java y C #) ofrecen Function/Method Overriding. Los lenguajes como Python le ofrecen pasar (número ilimitado de) argumentos y argumentos de palabras clave a una función:

    Java:

    public void disp(char c)
    {
         System.out.println(c);
    }
    
    public void disp(char c, int num)  
    { 
         System.out.println(c + " " + num);
    }
    
    disp("A")
    disp("A", 3)

    Pitón

    def disp(c, *args):
        if args:
            num = args[0]
            print("%s %f" % (c, num))
        else:
            print("%s" % c)
    
    disp("A")
    disp("A", 3)
  • Muchos idiomas ofertas public, privatey protectedmétodos. Puede manejar la llamada de función en una publicfunción y hacer el trabajo en private/protectedfunciones.

    En python, no existe una definición pública / privada para métodos y funciones, pero un guión bajo ( _) indica que un método es privado y no debe usarse externamente. Las llamadas API externas se realizan mediante un método abierto al mundo exterior y todas las tareas se realizan en las llamadas funciones locales :

    def format_input(a, b, *args, **kwargs):
        # This function is open to anyone. so use *args and **kwargs to get
        # all possible available arguments. Do not change this function
        # definition and function parameters
        a = _evaluate_input(a)
        b  =_evaluate_input(b)
        # c is not used by all APIs and may not documented in all external
        # API docs. So chech if it was sent as a keyword argument. If yes
        # evalaute it too
        if "c" in kwargs:
            c  =_evaluate_input(kwargs.get("c"))
        _check_extra_arguments(args)
        _check_extra_keyward_arguments(kwargs)
    
    def _evaluate_input(value):
        # This is a private method thus should not be called from the
        # outside world. You can change this method and parameter structure 
        # to fit your needs and do not care for outside world since no
        # outer API should use this function directly.
        ...
    
    def _check_extra_arguments(value):
        # We will check if any extra argument is passed and handle them accordingly
        ...

Como dije, una definición de API que (también) es utilizada por aplicaciones externas no debería cambiar con tanta frecuencia. Puede buscar formas de hacer que sus funciones externas sean más flexibles para que pueda cambiar cómo funciona la API sin romper el estado actual.

Ángel caido
fuente
1

Nuestro problema es que cuando hacemos un cambio en un módulo, debemos asegurarnos de actualizar todos los demás módulos que acceden a este código. Esto es difícil de saber porque está en diferentes bases de código.

Sugeriría que esto es imposible de saber.

Usted es responsable de los Componentes y sus Interfaces.
Usted no es responsable de nada y de todo lo que pueda hacer uso de ellos.

¿Cómo podemos documentar dónde están todos los usos externos de una API? ¿O de lo contrario evitará que pequeños cambios rompan otros módulos?

¿Respuesta corta? Pruebas

Escribir pruebas que ejerciten las interfaces publicadas. Vuelva a ejecutarlos cada vez que realice un cambio. Mientras las pruebas "Pasen", no has roto nada. Cuando una prueba se rompe (y lo hará) ya sea (a) encontrar y solucionar el problema o (b) si puede justificar el cambio como legítimo, luego vuelva a escribir la prueba para acomodarlo.

Phill W.
fuente
0

He visto y codificado API con números de versión en las rutas y / o nombres de funciones.

De esta manera, puede tener diferentes versiones de API disponibles: API completas y diferentes versiones de las funciones dentro de una API.

Esto pone el trabajo de mantener todas las versiones de API en el código para la API; no es necesario cambiar el código de otras aplicaciones que no sea para el que se produjo la nueva instalación de API.

Creo que esto es especialmente importante al escribir API que serán utilizadas por aplicaciones fuera de su organización.

Como ejemplo, aquí hay una muestra de código para enviar un SMS usando la api de bulksms:

http://developer.bulksms.com/eapi/code-samples/csharp/send_sms/

a partir de ahí está la línea:

string url = ".../submission/send_sms/2/2.0";

donde el 2 y el 2.0 son números de versión de la API.

Como esta API está destinada a ser utilizada por muchos clientes de SMS masivos, un cambio en esta API podría romper muchas aplicaciones y hacer que el teléfono de soporte suene sin problemas.

Mike Vincent
fuente