Se eu tivesse que resumir o trabalho de um programador em poucas palavras, diria que somos "resolvedores de problemas". No entanto, não é tão simples. Alguns problemas são fáceis, outros são difíceis e, em ambos os casos, um conjunto específico de habilidades é necessário.
Ao abordar problemas de programação, há uma habilidade que está (ou deveria estar) sempre presente: a capacidade de abstração. Dijkstra classifica isso como a atividade mais vital de um programador competente. Ele menciona a abstração não como um termo vago, mas como uma ferramenta para lidar com a complexidade.
Embora eu não seja versado o suficiente para ensinar ou explicar abstração sem ser muito abstrato, gostaria de ressaltar que os matemáticos são muito bons nisso. Observe alguns deles e como resolveram alguns problemas clássicos.
É interessante ver que eles têm uma metodologia. Eles têm certeza sobre os dados que possuem, sobre o que estão procurando e são especialmente bons em reconhecer padrões (saber onde fazer uma substituição inteligente ou aplicar um teorema).
Humildemente, acho que uma boa maneira de melhorar sua capacidade de abstração é imitar esse comportamento. Para isso, você precisa de um sistema para resolver qualquer problema de programação. Vamos construir um.
Um Problema Para Resolver
Dar a você uma lista não é o ideal. É muito melhor ver o sistema funcionando enquanto o aplicamos a um problema. Vamos pegar um problema simples para resolver.
O problema é:
Você é um programador trabalhando em um sistema de estacionamento (implementado usando Node.js e PostgreSQL). Na implementação atual, os usuários podem agendar reservas, fornecendo uma data de início e uma de término. O horário não é considerado, pois todas as datas devem ser contadas como dias inteiros.
Seu trabalho agora é adicionar a restrição do estacionamento, já que o local possui apenas 5000 vagas e a aplicação não deve permitir overbooking.
Tenha Um Plano
A maneira mais fácil de enfrentar um problema é seguir um plano. Um plano com todas as etapas definidas dá a você uma direção e permite validar cada passo e, se necessário, mudar o curso; tudo sob seu controle.
A parte difícil é criar esse plano. É aí que muitos de nós, desenvolvedores de software, falhamos.
Pegando emprestados princípios do livro How To Solve It, de George Polya, temos quatro coisas a fazer ao resolver um problema: compreender o problema, elaborar o plano, executar o plano e olhar para trás.
Embora seja um livro sobre a resolução de problemas matemáticos, acho que funciona, com algumas mudanças, muito bem para problemas de software – tenho feito coisas semelhantes em minhas contribuições para o codebase do Node.js.
Entendendo O Problema
A primeira coisa é compreender o problema. Parece óbvio, mas na verdade não é. Às vezes, você é mecânico demais ao analisar um problema e simplesmente presume que entendeu. É um erro. Você pode confiar na sua intuição, mas precisa confirmar.
Para obter a compreensão total, faça a si mesmo as perguntas certas: o que estou tentando encontrar?, o que eu tenho? e quais são as condições/restrições?
Durante esta fase, você também pode introduzir qualquer notação que considere útil.
Pegue o problema proposto. Para evitar o overbooking, é necessário verificar a reserva que está sendo agendada contra as reservas já existentes que possam se sobrepor.
- o que temos:
reservationque se tenta agendar. Umreservationpossui camposstart_ateend_at, ambos sendoDate;reservationListé a lista de reservas já existentes para comparar comreservation.
- restrições:
- as datas ocupam o dia inteiro;
- as vagas de estacionamento são limitadas a 5000.
- Node.js e PostgreSQL
- o que precisamos encontrar: um algoritmo que receba o
reservatione determine se ele pode ser agendado ou não;
A notação introduzida de reservation e reservationList torna um pouco mais fácil entender o que está acontecendo. Com isso, você tem uma compreensão melhor do que precisa ser feito.
Elaborando O Plano
Uma vez que você raciocinou sobre o que precisa ser feito, é hora de planejar como você fará isso. Para isso, você pode:
- observar as múltiplas partes do problema, verificar os dados e as restrições novamente;
- resgatar da memória qualquer outro problema que pareça semelhante;
- tentar reformular o problema original para torná-lo mais simples;
- remover/relaxar algumas condições/restrições para ter uma noção de quais subproblemas estão envolvidos.
Analisar problemas sob várias perspectivas permite simplificar a complexidade, fazendo com que caibam no seu cérebro com menos esforço.
Depois de pensar um pouco sobre este problema e suas condições, eu poderia reformulá-lo, já usando a notação introduzida na etapa anterior, para:
Sua tarefa é escrever uma função que receba o
reservationListereservation, encontre os dias sobrepostos e verifique se todos eles têm menos de 5000 reservas. Se todos os dias nesse período tiverem menos de 5000,reservationpode ser agendado.
Para simplificar o plano e sua execução, vamos relaxar a restrição do PostgreSQL e assumir que estamos trabalhando com dados em memória.
Nosso plano é então:
- Pegar o
resevationereservationListe encontrar os dias sobrepostos; - Calcular quantas reservas existem para cada dia na sobreposição;
- Determinar se o
reservationpode ser agendado;
Para isso, vamos usar o objeto de reserva como:
type Reservation = {
start_at: Date;
end_at: Date;
};Execute O Plano
Nosso plano é simples porque temos um problema pequeno. O que fazemos agora é percorrer cada etapa e garantir que todas estejam corretas.
Passo 1 – Encontrando Sobreposição De Dias
Encontrar sobreposições é um problema já resolvido, e podemos aproveitar a solução.
type Tuple = [Date, Date];
type OverlappingDates = Tuple[];
const getOverlapping = (reservationList: Reservation[], reservation: Reservation): OverlappingDates => {
const overlappingTuples = [];
for(const r of reservationList) {
const hasOverlapping = r.start_at <= reservation.end_at && r.end_at >= reservation.start_at;
if (!hasOverlapping) continue;
overlappingTuples.push([r.start_at, r.end_at]);
}
return overlappingTuples;
};NOTA: O código aqui será imperativo para torná-lo o mais claro possível para qualquer pessoa.
Após cada etapa, você prova que este passo foi executado corretamente. Matemáticos os provam usando truques matemáticos, e nós fazemos isso escrevendo testes.
Garantindo Que O Passo 1 Está Correto
Vamos escrever alguns testes para garantir que tudo esteja funcionando conforme o esperado.
import assert from 'node:assert';
const reservationList = [
{ start_at: new Date('2025-08-01'), end_at: new Date('2025-08-03') },
{ start_at: new Date('2025-08-01'), end_at: new Date('2025-08-02') },
{ start_at: new Date('2025-08-03'), end_at: new Date('2025-08-04') },
{ start_at: new Date('2025-08-05'), end_at: new Date('2025-08-07') },
];
const overlapping1 = getOverlapping(reservationList, {
start_at: new Date('2025-08-01'),
end_at: new Date('2025-08-03')
});
assert.deepStrictEqual(overlapping1, [
[new Date('2025-08-01'), new Date('2025-08-03')],
[new Date('2025-08-01'), new Date('2025-08-02')],
[new Date('2025-08-03'), new Date('2025-08-04')]
]);
const overlapping2 = getOverlapping(reservationList, {
start_at: new Date('2025-08-08'),
end_at: new Date('2025-08-10')
});
assert.deepStrictEqual(overlapping2, []); // there is no date overlapping the period from Aug 08 to Aug 10Bastante simples. Note que todos os períodos usados já estão normalizados, definindo apenas a data sem a hora. Isso é algo que você deve fazer em sua aplicação.
Passo 2 – Computar Quantas Reservas Existem Em Cada Dia
Tal função nos daria uma lista de reservas que se sobrepõem àquela que estou tentando agendar. O terceiro passo do nosso plano nos pede para verificar quantas reservas existem por dia.
const addDay = (original: Date) => {
const s = new Date(original);
s.setTime(s.getTime() + 24 * 60 * 60 * 1000);
return s;
};
const getReservationsCountPerDate = (overlappingDates: OverlappingDates) => {
const group = {};
for (const [start_at, end_at] of overlappingDates) {
let s = start_at;
while (s <= end_at) {
const k = s.toISOString().split('T')[0];
if (s >= start_at && s <= end_at) {
if (!group[k]) group[k] = 0;
group[k] += 1;
}
s = addDay(s);
}
}
return group;
};Lembre-se de provar que este passo está correto com testes.
Passo 3 – Determinar se a reserva ( reservation ) Pode Ser Agendada
A partir dos dois passos, você já tem tudo agrupado por dia. Agora é a hora de verificar se todos os dias sobrepostos estão livres.
const canSchedule = (maxSpots, reservationList, reservation) => {
const overlappingDates = getOverlapping(reservationList, reservation);
const totalReservationsByDate = getReservationsCountPerDate(overlappingDates);
return Object.values(totalReservationsByDate).every(n => n < maxSpots);
};Novamente… garanta que esteja correto.
Revisando
O trabalho ainda não terminou. Neste momento, você deve olhar para trás, para todo o plano, e confirmar se encontrou o que procurava, se todos os requisitos foram atendidos e se existem maneiras de simplificar a solução.
Uma simplificação pode ser feita no passo 2, por exemplo. Ao agrupar, ele pega os períodos sobrepostos, embora algumas datas não se sobreponham de forma alguma. Aqui está um exemplo para ilustrar:
Suponha que a reservationList contenha uma amostra que vai de 01 de agosto a 10 de agosto. Ao tentar agendar uma reserva de 09 a 10 de agosto, não há necessidade de contar as amostras da reservationList de 01 a 08 de agosto.
Reformulando: a amostra da reservationList se sobrepõe, mas não necessariamente todos os seus dias.
Outra coisa a se pensar é o que acontece se restar apenas uma vaga e dois clientes tentarem agendá-la simultaneamente. A solução que você criou lida com isso? Se não, o que pode ser feito? É um subproblema; você pode aplicar este mesmo sistema novamente e resolvê-lo.
Torne Isso Real
Como relaxamos os requisitos do banco de dados, tivemos um plano para um subproblema. Neste momento, podemos olhar para trás e trazer essa restrição de volta. Para este problema especificamente, não deve ser tão difícil traduzi-lo para SQL.
Dica: Você pode ter uma query para coletar as tuplas sobrepostas e continuar com os passos 2 e 3, ou pode ir além e ter uma única query cuidando das duas primeiras partes do plano.
Conclusão
Trabalhar neste sistema exige pensar, o que pode ser difícil, mas contanto que você continue fazendo isso, repetindo esse comportamento, você ficará melhor em cada fase da resolução do problema.
Isso melhorará sua capacidade de análise e abstração, permitindo que você resolva qualquer problema de programação.
We want to work with you. Check out our Services page!

