Tan importante es el qué como el cómo. Ya sé, parece un trabalenguas, pero voy a tratar de explicarlo. Cuando le pides a un asistente que te genere un código, debes ser lo más específico posible, para que haga lo que tú quieres. Hasta aquí todos de acuerdo.
Pero, tratándose de programación, aún hay mucha libertad para elegir el lenguaje, paradigma, la estructura, convenios etc. Por eso, es fundamental que le guíes y restrinjas las opciones. Es una manera de agregar contexto que garantice una respuesta más cercana a lo que deseas.
Ese contexto puedes enviarlo en cada prompt
, o en configurarlo para que el asistente sepa de qué estás hablando. Dependerá un poco de la herramienta que utilices, pero en este post veremos cómo hacerlo en Cursor y con GitHub Copilot.
Reglas generales
Si todos tus proyectos comparten similares características, puedes establecer esas reglas en el asistente una sola vez. Pero lo más probable es que necesites configurarlo para cada proyecto.
Veamos como tratan estas herramientas las reglas, y a partir de ahí, veremos cómo configurarlas.
Reglas generales en Cursor
Cursor fue el primero en agregar esta funcionalidad, y lo hizo de una manera muy sencilla. Las reglas se establecían en la pestaña Settings
de Cursor, en un recuadro de texto que no invitaba al crecimiento descontrolado.
El sentir de la comunidad de desarrolladores es que esas reglas se parecían mucho al concepto de system prompt
de las IA. A este respecto ya he dedicado un post: System prompt, configura tu asistente.
Te lo resumo, define un contexto lo más general posible que te pueda servir para cualquier proyecto. Al mismo tiempo, no olvides que es un contexto, con su peso en caracteres, digo en tokens
. Yo voy a lo más práctico y sigo el ejemplo de Íñigo Montoya en La Princesa prometida:
Soy un ingeniero de software senior que trabaja como freelance en proyectos de consultoría y formación para programadores.
Escribo código limpio, probado y bien documentado, usando patrones de diseño y arquitecturas de software adecuadas al tamaño del proyecto.
Actúa como un experimentado especialista con un profundo conocimiento de lenguajes y tecnologías de programación.
Ayúdame a desarrollar ejemplos y tutoriales claros y bien documentados, y a hacer correcciones y revisiones de código para mis alumnos y clientes.
Para ello, cumple con las siguientes instrucciones:
- Contéstame en inglés, aunque te pregunte en español.
- Se escueto y directo, sin preámbulos ni despedidas educadas.
- No me expliques fundamentos ni conceptos básicos.
- Antes de contestar, lee bien toda la pregunta, prepara una respuesta, evalúa, corrige y luego responde.
- Si no sabes la respuesta, no inventes una; pregúntame para poder encontrarla juntos.
- Completa todas las tareas que se te encarguen, sin dejar nada sin hacer.
- Ajústate a las reglas concretas de cada proyecto, o usa los estándares o mejores prácticas de los que dispongas.
Reglas generales en GitHub Copilot
GitHub Copilot no distingue entre reglas generales y reglas por proyecto, al menos no de una forma directa. Las instrucciones, como las llama Copilot, se establecen en ficheros, que debes crear en tu proyecto.
Y a eso vamos, a crear ficheros de instrucciones para nuestros proyectos, tanto con Cursor como con GitHub Copilot.
Reglas por proyecto
Como decía, ambas herramientas nos permiten crear ficheros especiales que usaran como contexto para nuestros prompts.
Reglas por proyecto en Cursor
El fichero de reglas en Cursor está completamente determinado, se llama .cursorrules
, y se guarda en la raíz de tu proyecto.
Punto, son las reglas de Cursor.
En ningún lugar se especifica su formato, pero se supone que es un fichero de texto plano, y para facilitar su lectura humana, se suele usar el formato Markdown.
Reglas por proyecto en GitHub Copilot
En Copilot, no hay un fichero de reglas específico, sino varios! Ya; es un poco sorprendente, pero en el momento que escribo este post, es una característica que aprece como preview o experimental. Así que supongo que quieren ver como y cuanto se usan estas opciones.
En principio, fuerza a crear un fichero copilot-instructions.md
en la carpeta especial .github
. Por tanto, tenemos de nuevo un convenio propio no configurable para esta herramienta. Eso sí, no dejan tampoco lugar a dudas sobre su contenido; debe ser un fichero Markdown.
Pero, hay más ficheros. Lo anterior aplica a la generación de código de forma general. Y resulta que Copilot permite que puedas dar instrucciones específicas para generar código, pero también para revisiones de código o incluso para testing.
En estos casos, puedes indicarle las instrucciones como un breve texto, o como un fichero en tu proyecto. Lo cual deja abierta la posibilidad de usar cualquier fichero de tu proyecto como contexto.
Como te digo, estas opciones están marcada como experimentales, así que no es de extrañar que no estén muy claras, estén duplicadas y puedan cambiar o desaparecer en cualquier momento. Así que vamos a usar solamente lo que parece más estable, el fichero .github/copilot-instructions.md
.
Ejemplos de reglas por proyecto
Las guías que dan las herramientas para establecer las instrucciones son bastante etéreas, así que no es fácil dar consejos generales. Basándome en mi experiencia y los ejemplos que he visto, te resumo los que considero más importantes:
1 - El tamaño importa. Mantén tus instrucciones lo más cortas posibles.
2 - El contexto es El Rey. Restringe la libertad de la IA para que use tus preferencias.
3 - Un ejemplo es mejor que mil palabras.
Ejemplo para un proyecto Node Express con TypeScript
# Dependencias de herramientas y versiones
- Node 22
- Express 5
- TypeScript 5
- ESLint 9
- Prettier 3
- Jest 29
# Reglas de estilo y buenas prácticas
- Agrega JSDoc a todos los miembros públicos de las clases y a todas las exportaciones.
- Usa el prefijo `#` para los miembros privados.
- Agrega guardias a los métodos que reciben datos de fuentes externas.
- No uses `null` como valor, y define valores por defecto para las estructuras de datos.
- No uses `any` como tipo, y define siempre tipos concretos.
- Evita el uso de `enum` y usa `type` para representar dominios de valores.
- Usa `interface` para definir comportamientos de objetos.
- No uses números mágicos y define constantes para los valores.
- No anides estructuras repetitivas o condicionales, usa funciones auxiliares.
- Mantén las funciones y métodos simples, de menos de 20 instrucciones.
# Estructura de ficheros y carpetas
- Cada fichero exporta un único elemento, de un tipo con el convenio `nombre.tipo.ts`.
- Los tipos de fichero son: `controller`, `dto`, `entity`, `middleware`, `repository`, `service`, `type`, `util`.
- Hay tres carpetas raíz: `api`, `middleware` y `shared`.
# Reglas de testing
- Usa Jest para escribir tests.
- Escribe un test para cada método o función pública.
- Escribe tests para todas las rutas de tu API.
- Sigue el convenio `Arrange-Act-Assert` para organizar el código de los tests.
- Agrupa en `describe` los tests de un mismo fichero o módulo.
- Usa `beforeAll` para inicializar variables o contextos compartidos entre tests.
- Nombra las variables de los tests usando el convenio `inputNombre`, `mockNombre`, `actualNombre`, `expectedNombre`.
- Usa datos realistas y reutilízalos en varios tests.
# Ejemplo
```typescript
// src/models/user.model.ts
/**
* Modelo de usuario
*/
export interface User {
id: string | undefined;
name: string;
email: string;
}
// src/models/user-token.model.ts
/**
* Modelo de token de usuario
*/
export interface UserToken {
userId: string;
token: string;
}
// src/controllers/user.controller.ts
/**
* Controlador de usuarios
* @requires UserService
*/
export class UserController {
#userService: UserService = new UserService();
/**
* Crea un nuevo usuario
* @param user - Usuario a crear
* @returns Token de usuario creado
* @throws Error si el nombre o el email no son válidos
*/
postNewUser(user: User): UserToken {
if (!user.name || !user.email) {
throw new Error("Name and email are required");
}
return this.#userService.createUser(user);
}
}
// src/services/user.service.ts
/**
* Servicio de usuarios
* @requires UserRepository
*/
export class UserService {
#userRepository: UserRepository = new UserRepository();
createUser(user: User): UserToken {
const userId = this.#userRepository.saveUser(user);
const token = generateToken(userId);
return { userId, token };
}
}
// src/repositories/user.repository.ts
/**
* Repositorio de usuarios
* - Guarda en memoria los usuarios
*/
export class UserRepository {
#users: User[] = [];
#STRING_BASE = 36;
#ID_PREFIX = 2;
#ID_LENGTH = 15;
/**
* Guarda un usuario en memoria
* @param user - Usuario a guardar
* @returns Identificador del usuario guardado
*/
saveUser(user: User): string {
const id = this.#generateId();
user.id = id;
this.#users.push(user);
return id;
}
#generateId(): string {
return Math.random().toString(this.#STRING_BASE).substring(this.#ID_PREFIX, this.#ID_LENGTH);
}
}
// src/shared/utils/token.util.ts
/**
* Utilidad para generar tokens
* @param userId - Identificador del usuario
* @returns Token generado
*/
export function generateToken(userId: string): string {
const random = Math.random();
return `${Date.now()}_${random}_${userId}`;
}
```
Prioriza siempre el código mantenible, robusto, probado y documentado.
Conclusión
Como ves, las instrucciones para un proyecto son esenciales para controlar el comportamiento de la IA. Pero no olvides que son instrucciones, y que la IA no siempre las interpreta como tú esperas, al pie de la letra. Así que no te quedes con una sola experiencia, sino que como simpre con la IA, prueba y evalúa.
Para empezar, hay un par de sitios en los que puedes buscar inspiración:
Aunque en principio, parezcan destinadas a Cursor, puedes usarlas igualmente para GitHub Copilot. Recuerda en qué ruta van en cada caso:
.cursorrules
para Cursor.github/copilot-instructions.md
para GitHub Copilot
Como siempre, espero que te haya sido de utilidad este post, y que te ayude a cumplir con el lema de la Artificial Intelligence Drive Development en AIDDbot
code smarter, not harder