Hace algunos años escuché una frase que quedó marcada en mi corazón: “¿Por qué hacer las cosas difíciles si las podemos hacer fáciles?”. La persona que me lo dijo se refería a cómo a veces complicamos nuestro trabajo, cuando en realidad existen formas más simples y eficientes de lograr lo mismo.

En los últimos días he estado trabajando en transformar mi proyecto Capstone de BYU-Idaho a Nest.js (Backend), y el uso del CLI ha sido clave. Me ha permitido generar módulos, controladores y servicios de forma rápida. Si hubiera escrito todo línea por línea, me habría tomado mucho más tiempo. La clave está en aprovechar las herramientas disponibles para mejorar y acelerar el desarrollo de software.

¿Cómo mejorar tu productividad como desarrollador?

1. Usa el CLI para aprender y producir más rápido

Cuando estás aprendiendo un framework, como Nest.js, usar su CLI es una gran idea. Te ayuda a entender la estructura del proyecto, cómo se organizan los módulos, controladores y servicios. Una vez que comprendas los conceptos básicos, puedes seguir usándolo para ahorrar tiempo y enfocarte en mejorar otras áreas del código.

productividad como desarrollador

2. Mejora las funciones de tu código

En artículos anteriores hablé sobre el principio “Don’t Repeat Yourself” (DRY). En mi proyecto actual, he creado servicios reutilizables como:

productividad como desarrollador

– Generador de códigos OTP

import { Injectable } from '@nestjs/common';

@Injectable()
export class OtpCodeService {
  constructor() {}

  generateOtpCode(maxLength: number = 6): string {
    const min = Math.pow(10, maxLength - 1); // Mínimo valor para la longitud especificada
    const max = Math.pow(10, maxLength) - 1; // Máximo valor para la longitud especificada
    return Math.floor(min + Math.random() * (max - min + 1)).toString();
  }
}

– Hashing y verificación de contraseñas

import { Injectable } from '@nestjs/common';
import * as bcrypt from 'bcryptjs';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class HashPasswordsService {
  constructor(
  ) {}

  async hashPassword(passwordToHash: string, security: number = 12): Promise<string> {
    const hashedPassword = await bcrypt.hash(passwordToHash, security);
    return hashedPassword;
  }

  async verifyPassword(dataBasePassword: string, passwordToCheck: string): Promise<boolean> {
    return await bcrypt.compare(passwordToCheck, dataBasePassword);
  }
}

– Validadores de formato de email

import { Injectable } from '@nestjs/common';

@Injectable()
export class EmailValidatorService {
  isValid(email: string): boolean {
    // Basic regex for validating an email address
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  }
}

– Envío de correos electrónicos

import { Injectable } from '@nestjs/common';
import * as nodemailer from 'nodemailer';
import { EmailLog } from './models/email-log.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class MailService {
  private transporter: nodemailer.Transporter;

  constructor(
    @InjectRepository(EmailLog)
    private emailLogRepository: Repository<EmailLog>,
  ) {
    this.transporter = nodemailer.createTransport({
      host: process.env.NODEMAILER_HOST,
      port: process.env.NODEMAILER_PORT ? parseInt(process.env.NODEMAILER_PORT) : 587,
      secure: process.env.NODEMAILER_SECURE === 'true', // <-- CORRECTO
      auth: {
        user: process.env.NODEMAILER_USER,
        pass: process.env.NODEMAILER_PASSWORD
      },
      connectionTimeout: 100_000,
      greetingTimeout: 100_000,
      socketTimeout: 100_000
    });
  }

  async sendEmail(emailTo: string, emailSubject: string, emailBody: string): Promise<{sent: boolean, error?: string | null}> {
    const emailLog = new EmailLog();
    emailLog.emailTo = emailTo;
    emailLog.emailSubject = emailSubject;
    emailLog.emailBody = emailBody;


    try {
      const mailOptions = {
        from: process.env.NODEMAILER_USER,
        to: emailTo,
        subject: `${emailSubject} | ${process.env.APP_NAME} System`,
        html: `
          <h1 style='text-align: center;'>${emailSubject}</h1>
          <hr />
          ${emailBody}
          <hr />
          <p style='text-align: center;'><strong>${process.env.APP_NAME}®</strong> System </br>© ${new Date().getFullYear()} <a href="https://marbust.com" style="font-weight: bold;">Marbust Technology Company</a> - All Rights Reserved</p>
        `,
      };
      //console.log(`Data for transporter: ${JSON.stringify(this.transporter)}`);
      const info = await this.transporter.sendMail(mailOptions);
      console.log("Info: ", info);
      console.log(`Email was sent to: ${emailTo} with Id: ${info.response}`);
      emailLog.result = true;
      emailLog.messageId = info;
     
      await this.emailLogRepository.save(emailLog);
      return {
        sent: true,
        error: null,
      }
    } catch (error) {
      console.log(error);
      emailLog.result = false;
      await this.emailLogRepository.save(emailLog);
      return {
        sent: false,
        error: 'Error sending email',
      }
    }
  }

  async sendTestEmail(request: any): Promise<{sent: boolean, error?: string}> {
    const { secret, email } = request;
    if (!secret || !email) {
      return {
        sent: false,
        error: 'Secret and email are required',
      }
    }
    if (secret !== process.env.JWT_SECRET) {
      return {
        sent: false,
        error: 'Invalid secret',
      }
    }
    // Puedes personalizar el asunto y cuerpo del correo
    const {
      sent,
      error
    } = await this.sendEmail(
      email,
      'Test Email',
      'This is a test email sent from the /mail/test endpoint.'
    );
    if (sent) {
      return {
        sent: true
      }
    } else {
      console.error('Error sending test email:', error);
      return {
        sent: false,
        error: 'Error sending test email',
      }
    }
  }
}

– Firma y validación de JWTs

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { JwtGenerated } from './models/jwt-generated.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService,
    @InjectRepository(JwtGenerated)
    private readonly jwtGeneratedRepository: Repository<JwtGenerated>,
  ) {}

  async signPayload(payload: any, secretKey?: string): Promise<string> {
    // Save the payload in the database
    const jwtGenerated = new JwtGenerated();
    jwtGenerated.payload = JSON.stringify(payload);
    jwtGenerated.expiration = new Date(Date.now() + 3600000);
    await this.jwtGeneratedRepository.save(jwtGenerated);

    return this.jwtService.sign(payload, {
      secret: secretKey ? secretKey : process.env.JWT_SECRET,
      expiresIn: '1h',
    });
  }

  verifyToken(token: string, secretKey?: string): any {
    try {
      return this.jwtService.verify(token, { secret: secretKey ? secretKey : process.env.JWT_SECRET });
    } catch (err) {
      if (err.name === 'TokenExpiredError') {
        console.error('Token has expired.');
      } else if (err.name === 'JsonWebTokenError') {
        console.error('Invalid token.');
      } else {
        console.error('Token verification failed.');
      }
      throw new Error('Token verification failed.');
    }
  }
}

– Validación de roles de usuario

// filepath: d:\Repositories\MarbustSystem\src\services\check-access\check-access.service.ts
import { Injectable, ForbiddenException } from '@nestjs/common';

@Injectable()
export class CheckAccessService {
  checkAccess(decodedToken: any, minimumRole: number): void {
    console.log(">>> Inside checkAccess, decodedToken: ", decodedToken);
    console.log(">>> Inside checkAccess, minimumRole: ", minimumRole);
    if (!decodedToken) {
      throw new ForbiddenException('Not authenticated.');
    }

    if (!decodedToken.roleId) {
      throw new ForbiddenException('Role not found in JWT.');
    }

    if (decodedToken.roleId < minimumRole) {
      throw new ForbiddenException('Unauthorized access.');
    }
  }
}

– Inserción de datos en lote en la base de datos

import { Injectable, OnModuleInit } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Role } from '../../users/models/role.entity';

@Injectable()
export class RoleService implements OnModuleInit {
  constructor(
    @InjectRepository(Role)
    private readonly roleRepository: Repository<Role>,
  ) {}

  async onModuleInit() {
    const roles = [
      { name: 'User' },
      { name: 'Supervisor' },
      { name: 'Admin' },
      { name: 'Superadmin' },
    ];

    for (const role of roles) {
      const existingRole = await this.roleRepository.findOne({ where: { name: role.name } });
      if (!existingRole) {
        await this.roleRepository.save(role);
      }
    }
  }
}

Este enfoque te permite reutilizar código en distintos controladores y mantener todo más limpio y eficiente.

3. Aprovecha GitHub al máximo

No te limites a crear un repositorio y trabajar solo sobre la rama principal. Usa GitHub como una herramienta profesional:

  • Crea issues para funcionalidades específicas
  • Asígnate tareas
  • Crea ramas para cada funcionalidad
  • Realiza pull requests con seguimiento del proceso

Esto no solo te ayuda a organizarte mejor, sino que también mejora tu experiencia y documentación del proyecto.

productividad como desarrollador

4. Usa Copilot y otras herramientas de IA

GitHub Copilot es una herramienta poderosa (¡y gratuita para muchos estudiantes y desarrolladores!) que puede ayudarte con autocompletaciones y sugerencias inteligentes. No dependas al 100%, pero sí úsalo como apoyo. Te puede ahorrar tiempo, darte ideas y mejorar tu productividad.

productividad como desarrollador

5. Encuentra un equilibrio en tu vida

Desarrollar puede ser adictivo y muy gratificante, pero también es importante dedicar tiempo a tus seres queridos. El tiempo no vuelve. Mira una película, juega algo, sal con amigos. A veces, desconectarte también ayuda a pensar mejor, liberar estrés y encontrar nuevas ideas.

Conclusión

Espero que estas ideas te sirvan para convertirte en una mejor versión de ti como desarrollador. La felicidad también está en el equilibrio y en usar bien las herramientas que tienes a tu alcance.

Recuerda que subo videos ocasionalmente a mi canal de YouTube y escribo artículos como este en mi blog. ¡Te invito a darte una vuelta!


Sobre el Autor:

Marco Antonio Bustillos Quiroz
Bachelor of Science in Web Design and Development (BYU-Idaho)
Full Stack Web Developer con más de 7 años de experiencia en desarrollo Front End y 5 años en desarrollo Back End. Mis proyectos incluyen sitios web, landing pages, aplicaciones web, REST APIs, e-commerce, y mucho más. Mi pasión es crear experiencias digitales impactantes y compartir conocimientos con otros desarrolladores.