Usando el Operador Spread de TypeScript para Limpiar Código CDK

No tengo mucha experiencia con TypeScript—en realidad, nunca había tocado el lenguaje hasta hace poco cuando empecé a escribir Infrastructure as Code usando CDK. Hoy aprendí un truco útil que puede ayudar a limpiar secciones de tu stack poniendo configuración compartida en un objeto y luego usando el operador spread para fusionar esas configuraciones en los props de tus constructs.

El Problema: Configuraciones Lambda Repetitivas

Imagina que tienes un stack con un grupo de funciones Lambda que se ve algo así:

const firstFunction = new lambda.Function(this, "firstFunction", {
    runtime: lambda.Runtime.PYTHON_3_13,
    code: lambda.Code.fromAsset("lambdas/first_function", {
      bundling: <some_bundling_options>,
    }),
    handler: "first_function.handler",
    vpc: <some_vpc>,
    securityGroups: [sg_1, sg_2, sg_3, sg_n ],
    allowPublicSubnet: true,
    description: "Cool unique description for my first function",
    timeout: cdk.Duration.seconds(17)
  }
);

const secondFunction = new lambda.Function(this, "secondFunction", {
  runtime: lambda.Runtime.PYTHON_3_13,
  code: lambda.Code.fromAsset("lambdas/second_function"),
  handler: "second_function.handler",
  environment: {
    MY_ENV_VAR: <some_value>,
    SOME_OTHER_VAR: <some_value>,
    EVEN_MORE_VAR: <some_value>,
  },
  vpc: <some_vpc>,
  securityGroups: [sg_1, sg_2, sg_3, sg_n ],
  allowPublicSubnet: true,
  description: "Cool unique description for my second function",
  timeout: cdk.Duration.seconds(17)
});

const thirdFunction = new lambda.Function(this, "thirdFunction", {
    runtime: lambda.Runtime.PYTHON_3_13,
    code: lambda.Code.fromAsset("lambdas/third_function"),
    handler: "third_function.handler",
    environment: {
      ANOTHER_VAR: <some_value>,
    },
    vpc: <some_vpc>,
    securityGroups: [sg_1, sg_2, sg_3, sg_n ],
    allowPublicSubnet: true,
    description: "Cool unique description for my third function",
    timeout: cdk.Duration.seconds(17)
  }
);

Al mirar estas funciones Lambda, notarás que comparten muchas propiedades:

  • Todas usan el mismo runtime: PYTHON_3_13
  • Todas están adjuntas a la misma VPC
  • Todas usan los mismos grupos de seguridad
  • Todas tienen allowPublicSubnet establecido en true
  • Comparten el mismo timeout de 17 segundos

La Solución: Extraer Configuración Compartida

Para reducir la duplicación, puedes extraer estas opciones comunes a un objeto de configuración compartida:

const sharedConfig = {
  runtime: lambda.Runtime.PYTHON_3_13,
  vpc: <some_vpc>,
  securityGroups: [sg_1, sg_2, sg_3, sg_n ],
  allowPublicSubnet: true,
  timeout: cdk.Duration.seconds(17),
};

Luego, en cada llamada del constructor Lambda, usa el operador spread para fusionar la configuración compartida:

...sharedConfig

Es mejor ver cómo se ve en contexto, así que aquí está el código refactorizado:

const sharedConfig = {
  runtime: lambda.Runtime.PYTHON_3_13,
  vpc: <some_vpc>,
  securityGroups: [sg_1, sg_2, sg_3, sg_n ],
  allowPublicSubnet: true,
  timeout: cdk.Duration.seconds(17),
};

const firstFunction = new lambda.Function(this, "firstFunction", {
    ...sharedConfig,
    code: lambda.Code.fromAsset("lambdas/first_function", {
      bundling: <some_bundling_options>,
    }),
    handler: "first_function.handler",
    description: "Cool unique description for my first function",
  }
);

const secondFunction = new lambda.Function(this, "secondFunction", {
  ...sharedConfig,
  code: lambda.Code.fromAsset("lambdas/second_function"),
  handler: "second_function.handler",
  environment: {
    MY_ENV_VAR: <some_value>,
    SOME_OTHER_VAR: <some_value>,
    EVEN_MORE_VAR: <some_value>,
  },
  description: "Cool unique description for my second function",
});

const thirdFunction = new lambda.Function(this, "thirdFunction", {
    ...sharedConfig,
    code: lambda.Code.fromAsset("lambdas/third_function"),
    handler: "third_function.handler",
    environment: {
      ANOTHER_VAR: <some_value>,
    },
    description: "Cool unique description for my third function",
  }
);

Elige tu Configuración Compartida Sabiamente

Es importante pensar cuidadosamente sobre qué pones en la configuración compartida. No vale la pena extraer todo lo que aparece múltiples veces. El principio clave es agrupar las cosas que cambian juntas, y dejar en su lugar las cosas que probablemente no cambiarán.

Por ejemplo, runtime es un buen candidato para configuración compartida porque típicamente quieres actualizar el runtime para todas las funciones Lambda al mismo tiempo.

Sin embargo, algo como timeout podría ser mejor dejado en definiciones Lambda individuales. En este ejemplo, cada timeout fue establecido a 17 segundos, pero no hay una razón inherente por la que todos deberían tener el mismo timeout para siempre. Los timeouts dependen de las demandas individuales puestas en cada Lambda, así que probablemente es mejor dejar que cada función tenga su propio valor.

La distinción está entre entender qué realmente representa lógica duplicada versus qué es solo coincidentemente igual ahora mismo. Solo porque una configuración o pieza de código sea idéntica en el momento no significa necesariamente que esté destinada a permanecer duplicada—pueden ser comportamientos fundamentalmente independientes que probablemente evolucionarán en diferentes direcciones. Acoplarlos juntos podría causar problemas más adelante.

Concluyendo

Pero bueno, me desvío—¡el punto principal era compartir lo útil que es el operador spread de TypeScript! Seguiré aprendiendo y compartiendo descubrimientos en el camino.

Gracias por tomarte el tiempo de leer esto. ¡Espero que lo encuentres útil!

Juan Luis Orozco Villalobos

¡Hola! Soy Juan, un ingeniero de software y consultor que vive en Budapest. Me especializo en computación en la nube e IA/ML, y me encanta ayudar a otros a aprender sobre tecnología e ingeniería