A IBM entregou um sistema de controle de satelite da NASA com 0,1 defeitos por mil linhas de codigo. A media da industria na epoca ficava entre 10 e 50. Eles nao conseguiram isso contratando engenheiros mais inteligentes ou trabalhando mais horas. Conseguiram proibindo desenvolvedores de executarem seu proprio codigo.
Isso era Cleanroom software engineering, desenvolvido por Harlan Mills na IBM na decada de 1970. O nome vem da fabricacao de semicondutores, onde o objetivo e prevenir as condicoes que criam defeitos. Voce nao testa qualidade para dentro. Voce a projeta para dentro.
O problema: o testing encontra defeitos, nao os previne
A maior parte da engenharia de software assume um ciclo de escrever-testar-corrigir. Voce escreve codigo, executa, encontra bugs, corrige. Parece produtivo. Tambem e matematicamente garantido que deixa defeitos para tras.
O testing so pode provar a presenca de bugs, nunca sua ausencia. Dijkstra disse isso em 1969. Se seu codigo tem mil possiveis caminhos de execucao e sua test suite cobre cinquenta deles, voce tem 950 caminhos nao testados. A taxa media de defeitos da industria de 10-50 por KLOC nao e uma falha de rigor no testing. E o resultado previsivel de um processo que permite que defeitos existam.
Cleanroom inverte isso. Em vez de escrever codigo e depois testa-lo, voce escreve codigo que e correto por construcao.
Como o Cleanroom realmente funciona
O metodo tem tres praticas rigidas que trabalham juntas.
Primeiro, desenvolvimento incremental sob statistical process control. O projeto e dividido em pequenos incrementos, cada um adicionando um subconjunto bem definido de funcionalidade. Cada incremento passa por especificacao, design, verificacao e testing como uma unidade completa. A chave e que as taxas de defeitos sao medidas por incremento. Se um incremento excede sua meta de defeitos, o processo e interrompido e a equipe descobre o que deu errado com o metodo, nao com os individuos.
Segundo, design funcional-teorico usando box structures. Cada componente de software e definido em tres niveis:
- Black box: O comportamento externo, definido puramente como uma funcao matematica de entradas para saidas. Sem state, sem detalhe de implementacao.
- State box: A funcao interna de transicao de state. Qual state o componente mantem, e como ele muda?
- Clear box: A implementacao real, construida a partir de componentes verificados.
Cada nivel e verificado contra o superior antes de prosseguir. Voce nao escreve a Clear box ate que a State box seja provada correta. Voce nao escreve a State box ate que a Black box seja provada correta. Isso parece lento. Nao e, porque voce nao esta debuggando. Voce nao passa tardes em um debugger. O codigo funciona na primeira vez que compila.
Terceiro, e essa e a parte que explode cerebros: nenhum execution testing por desenvolvedores. As pessoas que escrevem o codigo nao o compilam, executam ou fazem unit testing. O testing e realizado por uma equipe separada usando metodos estatisticos. Eles tratam o software como uma Black box e geram casos de teste baseados em usage profiles, nao na estrutura do codigo.
Essa separacao nao e crueldade burocratica. E o mecanismo central que forca a correcao por construcao. Se voce sabe que nao pode executar seu codigo para “ver se funciona”, e forcado a pensar em cada caso antes de digita-lo. Voce nao pode confiar que o compiler pegue seus erros, ou em um rapido python script.py para revelar o bug obvio. Voce tem que estar certo.
Os numeros que importam
A IBM Federal Systems Division usou Cleanroom em multiplos projetos nas decadas de 1980 e 1990. Os resultados foram consistentes entre equipes, linguagens e dominios de aplicacao.
O sistema de controle de satelite da NASA: 0,1 defeitos por KLOC no teste final. Um sistema de faturamento COBOL: 0,3 defeitos por KLOC. Um sistema em tempo real Ada: 0,4 defeitos por KLOC. Compare com as medias da industria na epoca: 10-50 defeitos por KLOC no unit test, 5-10 ainda restantes na entrega.
As equipes Cleanroom tambem entregavam mais rapido. Os dados da industria na epoca mostravam que 50-70% do esforco de software ia para testing e debugging. As equipes Cleanroom gastavam esse tempo em design, e o codigo funcionava de primeira.
Por que quase ninguem usa
Se o Cleanroom e tao eficaz, por que todo mundo nao o faz?
Voce nao pode “so tentar” em uma sprint. O metodo e interdependente. O statistical process control so funciona se voce medir cada incremento. As box structures so funcionam se voce fizer a verificacao formal. A regra de nao execucao so funciona se for absoluta. A adocao parcial lhe da nenhum dos beneficios e todo o overhead.
O mercado tambem mudou. Nas decadas de 1970 e 1980, o software era entregue em fita. Um bug em production era caro de corrigir. Hoje entregamos over the wire, e podemos aplicar patch em minutos. O incentivo economico para software zero-defect enfraqueceu.
A maioria de nos tambem gosta de executar nosso codigo. O feedback loop imediato de escrever-executar-corrigir e gratificante. Cleanroom pede que voce atrase essa gratificacao ate ter pensado completamente no problema. Para muitos desenvolvedores, isso parece tentar escrever prosa sem ser permitido rele-la.
O que voce pode roubar sem adotar toda a religiao
Voce provavelmente nao pode implementar o Cleanroom completo na sua empresa. Seu gerente olharia estranho para voce. Mas voce pode adotar partes dele e obter beneficios reais.
Escreva a interface antes da implementacao. Defina entradas, saidas e preconditions antes de escrever o body. Declare o que a funcao promete, nao so o que ela faz.
# precondition: items is a non-empty list of comparable elements
# postcondition: returns the smallest element in items
# raises: ValueError if items is empty
def min_item(items: list) -> any:
if not items:
raise ValueError("items must not be empty")
smallest = items[0]
for item in items[1:]:
if item < smallest:
smallest = item
return smallest
Essa e uma funcao trivial, mas a disciplina escala. Para um exemplo mais complexo, defina uma state machine explicitamente antes de implementa-la.
from enum import Enum, auto
class ConnectionState(Enum):
DISCONNECTED = auto()
CONNECTING = auto()
CONNECTED = auto()
CLOSING = auto()
# Allowed transitions:
# DISCONNECTED -> CONNECTING (on connect())
# CONNECTING -> CONNECTED (on handshake complete)
# CONNECTING -> DISCONNECTED (on timeout/error)
# CONNECTED -> CLOSING (on close())
# CLOSING -> DISCONNECTED (on ack received)
# Any other transition is illegal and raises StateError
class StateMachine:
_transitions = {
ConnectionState.DISCONNECTED: {ConnectionState.CONNECTING},
ConnectionState.CONNECTING: {ConnectionState.CONNECTED, ConnectionState.DISCONNECTED},
ConnectionState.CONNECTED: {ConnectionState.CLOSING},
ConnectionState.CLOSING: {ConnectionState.DISCONNECTED},
}
def __init__(self):
self.state = ConnectionState.DISCONNECTED
def transition(self, new_state: ConnectionState) -> None:
if new_state not in self._transitions.get(self.state, set()):
raise StateError(f"Illegal transition: {self.state.name} -> {new_state.name}")
self.state = new_state
Ao codificar transicoes validas explicitamente, voce torna estados ilegais unrepresentable. O codigo nao pode entrar em um estado invalido porque a state machine o impede. Essa e a filosofia das box structures em miniatura.
Faca outra pessoa testar seu codigo. A separacao de autoria e verificacao e a ideia mais radical do Cleanroom e a mais transferivel. Quando voce testa seu proprio codigo, voce testa suas proprias suposicoes. Outra pessoa tentara o caso que voce nao considerou porque parecia “obvio” que nao aconteceria.
Meca defect density por unidade de trabalho. Acompanhe quantos bugs escapam de cada fase do seu processo. Se sua sprint consistentemente e entregue com integration bugs, o problema nao sao desenvolvedores descuidados. O problema e que seu processo permite que integration bugs existam. Corrija o processo.
Onde isso desmorona
Cleanroom nao e uma solucao universal. Funciona melhor quando os requirements sao estaveis e a correcao importa mais que o time-to-market. Funciona mal para desenvolvimento exploratorio e rapid prototyping, onde o objetivo e descobrir o que os usuarios querem em vez de implementar corretamente uma especificacao conhecida.
Tambem requer management buy-in. Voce nao pode fazer Cleanroom em segredo. O statistical process control requer coleta de dados em toda a equipe. A regra de nao execucao requer que a gerencia realmente a faca cumprir, mesmo quando os deadlines se aproximam e os desenvolvedores querem “so verificar rapidamente se isso funciona”.
A conclusao
Os 0,1 defeitos por KLOC da IBM nao foram um milagre. Foram a saida de um processo projetado para prevenir defeitos em vez de detecta-los. As praticas especificas - box structures, statistical testing e isolamento de desenvolvedores - podem parecer estranhas para a cultura de software moderna. Mas o principio subjacente e atemporal: e mais barato pensar antes de digitar do que debuggar depois.
Voce nao precisa adotar Cleanroom por atacado. Comece com uma funcao. Escreva seu contract antes de seu body. Pec a um colega para revisar a logica antes de executa-la. Acompanhe de onde vêm seus bugs e corrija o processo que os produz. Zero defects por KLOC provavelmente nao e seu objetivo. Mas ir de 50 para 5 e alcancavel, e o metodo e o mesmo. Pense primeiro. Digite segundo. Execute por ultimo.