A situação
Um grupo médico com quinze clínicas funcionava sobre oito coisas que não sabiam umas das outras: um site, um widget separado de marcações, um ficheiro Excel para os stocks, um segundo ficheiro para o laboratório, um programa de contabilidade, uma aplicação de assiduidade, um dashboard de publicidade e uma caixa de correio partilhada. Cada número que contava existia em pelo menos dois sítios, e onde não coincidiam ninguém sabia dizer qual era o correto.
Isto não é um problema de tecnologia. É um problema de negócio que aparece como um problema de tecnologia, e é exatamente a forma de trabalho em que sou melhor.
O que construí
Uma única plataforma Django, a servir todas as quinze localizações: o site público, as marcações, os registos dos pacientes, o CRM, a logística de laboratório, os stocks de vários armazéns, a contabilidade fiscal, os RH e o reporte sobre a despesa de publicidade. Tudo por trás de um único admin, com isolamento ao nível da clínica e da região para nove papéis de pessoal, de modo que uma rececionista numa cidade não consegue ver os pacientes de outra cidade e nem sequer tem de pensar porquê.
As funcionalidades são a parte banal. Três dos sistemas por baixo delas merecem ser descritos, porque são eles que fazem a diferença entre software que funciona no dia do lançamento e software em que ainda confias dois anos depois.
Um fluxo de consentimento que resistiria em tribunal
O consentimento médico ao abrigo do RGPD não é uma checkbox com um timestamp. O que tens de conseguir produzir, mais tarde, é exatamente o documento com que uma determinada pessoa concordou num determinado momento - não a tua versão de agora.
Por isso o fluxo gera o documento, mostra-o no ecrã, confirma a identidade com um código enviado por SMS e guarda o PDF renderizado juntamente com o texto, a língua em que foi apresentado, o endereço a partir do qual foi assinado e uma cadeia de hashes que o liga ao registo. Se alterares o modelo depois disso, muda o que o próximo paciente vê e nada do que os anteriores aceitaram.
A regra por baixo: o consentimento é dado a uma frase, não a um
boolean. Se o teu sistema guarda true, guardou a coisa
errada.
O stock como ledger, não como número
A maioria dos sistemas de stocks mantém uma coluna quantity em
que o código escreve. Essa coluna acaba por ficar errada - um race, um retry, um
clique errado, uma transferência aplicada a meio - e assim que fica errada já
não tens como descobrir porquê, porque a prova foi sobrescrita pela resposta.
Aqui a quantidade não é guardada. Cada receção, transferência, consumo e correção é um lançamento imutável com sinal, e o saldo de qualquer armazém é a soma dos lançamentos que lhe tocam. As correções acrescentam o inverso em vez de editarem o histórico. Isso faz da reconciliação uma propriedade do desenho e não uma tarefa mensal, e significa que a resposta a «porque temos onze destes?» está sempre disponível e sempre completa.
Mais abaixo na página encontras uma versão funcional da ideia.
Um calendário que não pode sobrepor duas marcações
As marcações de quinze clínicas sobrepõem-se constantemente, e a receção precisa de ver um dia inteiro de uma vez, sem que duas marcações ocupem alguma vez o mesmo lugar. Este é o problema de interval scheduling: ordenas pela hora de início e depois pões cada marcação na primeira coluna cuja marcação anterior já terminou.
O que faz com que valha a pena implementá-lo bem e não por aproximação é que a versão greedy é demonstravelmente ótima - o número de colunas que produz é exatamente o maior número de marcações que se sobrepõem num único instante, ou seja, um limite inferior rígido que nenhum algoritmo consegue bater. Podes ver isso a confirmar-se na demo abaixo.
A realidade romena
Faturação, o diário de caixa e drivers para as impressoras fiscais exigidas por lei. Próteses seguidas por estafeta entre a clínica e o laboratório. Assiduidade com um pequeno cliente desktop que envia um heartbeat. Nada disto é engenharia interessante e tudo junto faz a diferença entre um sistema que as clínicas usam e um que elas contornam.
A decisão que defenderia com mais força
Todas as dependências externas da plataforma fazem fail open. Se o fornecedor de SMS estiver em baixo, se a API de publicidade estiver a limitar os pedidos ou se o serviço de faturação der timeout, a marcação conclui-se na mesma, e o trabalho que falta entra em fila e fica marcado para um humano.
A alternativa - recusar uma marcação porque um terceiro não está disponível - significa externalizar a tua receita para o uptime de outra pessoa. As proteções internas funcionam ao contrário: os rate limits, a validação e o honeypot fazem fail closed, porque aí não tens a quem deitar as culpas.
O que faria de outra maneira
Construiria a camada de reporte mais cedo. Concebi o ledger e o calendário corretamente desde o início e deixei o reporte crescer aos bocados, o que significou que os primeiros três meses de perguntas da gestão foram respondidos com consultas escritas à mão por mim. Correto, mas lento, e devia tê-lo previsto.