Criando APIs de desenvolvimento de Software

Veja neste artigo como desenvolver APIs e quais são os conceitos fundamentais relacionados com o desenvolvimento de APIs de boa qualidade.

Primeiramente quando falamos de API devemos realmente saber o que significa este termo. API significa Application Programming Interface ou, em português, Interface de Programação de Aplicativos. Esta interface de programação é um conjunto de padrões de programação que permitem a construção de aplicativos e a sua utilização. API é um conceito relativamente amplo.

Não precisamos escrever um framework ou uma biblioteca compartilhada para escrever uma API. Mesmo se escrevermos uma simples classe que é consumida por um de nossos colegas na nossa empresa ou na nossa instituição de ensino, nós estamos de fato escrevendo uma API. Um exemplo claro disso é que caso esta classe desenvolvida para um colega tenha um método alterado ou deletado teremos o mesmo problema se acontecesse com um framework ou uma biblioteca compartilhada.

Na grande maioria das vezes uma API se comunica com diversos outros códigos interligando diversas funções em um aplicativo. Um exemplo de uma API muito utilizada é a API dos sistemas operacionais que possuem diversos métodos e se comunicam com diversos processos do sistema operacional.

No restante do artigo veremos melhor alguns conceitos fundamentais sobre APIs como porque criar uma API, a importância do versionamento, como podemos fazer uma boa API e como verificar a qualidade da API através de conceitos chaves como compreensibilidade, consistência, descoberta, facilidade em tarefas simples e preservação de investimento. Também veremos os tipos de desafios que temos na primeira versão de uma API, a importância da compatibilidade, como fazer uma API Orientada a Caso de Uso e Avaliações.

Porque Criar uma API

Criar uma API não é algo fácil e nem mesmo barato. Uma API consome muito mais trabalho do que um software sem uma API. Porém, com uma API podemos criar sistemas melhores e minimizar o entendimento deles.

Através do reuso também podemos nos concentrar no mais importante: a lógica da aplicação. Dessa forma, podemos reutilizar frameworks e bibliotecas construídos por terceiros. Por exemplo, ninguém escreve um banco de dados do zero, pelo contrário, reutilizamos o que já existe e alteramos o código fonte para evoluir o código e ou adapta-lo a alguma necessidade especial que temos no nosso projeto. Não é necessário saber tudo de uma API, basta que saibamos aquilo que atenda as nossas necessidades, para isso é sempre interessante possuirmos a documentação em mãos. A API minimiza o entendimento de todos os detalhes de um componente e assim serve como um “alicerce”.

Portanto, uma API é uma abstração da funcionalidade e da implementação interna de cada componente. Uma aplicação comum precisa lidar com diversas dessas APIs. Para citar algumas temos o Java com diversas APIs e utilitários tais como Ant, Maven, Tomcat, JUnit, JavaCC e muitos outros. Cada uma dessas bibliotecas tem suas próprias APIs.

Versionamento

O sistema de numeração utilizado para sistemas de software é baseado em números naturais com um ponto os separando. Este ponto é necessário para acomodar “não-linearidades” no desenvolvimento de software. Isso ocorre porque não existe uma direção única de desenvolvimento de um software, mas sim muitos ramos representando correção de bugs, correção de um bug que gerou outro bug, e assim por diante. Dessa forma, uma versão 1.1.1 significa que ela contém menos funcionalidades que uma versão 2.0 que foi lançada antes da versão 1.1.1.

Assim, temos que o versionamento de um software é parecido com uma árvore, conforme pode ser visto na Figura 1.

Figura 1. Versionamento em formato de árvore.

Cada parte de uma aplicação modular tem um número de versão, geralmente um conjunto de números naturais separados por pontos, tal como “1.34.8”. Quando uma nova versão é lançada, ela deve ter um novo e maior número de versão, como 1.34.10, 1.35.1 ou 2.0.

Além disso, também podemos expressar dependências com outros componentes especificando o nome do componente e versão mínima necessária. Podemos solicitar a presença de um parser XML maior que a versão 3.0, ou então o driver de uma base de dados com a versão 1.5 ou acima, etc. Isto assume que o sistema tem compatibilidade com essas APIs e funcionará perfeitamente com as mais novas versões desses componentes.

A cada novo lançamento é importante mantermos todos os contratos que funcionavam nas versões anteriores funcionando nas novas versões também. Outra situação importante é nos mantermos informados das alterações nas dependências externas.

Frequentemente um sistema de versões e dependências precisa de um gerente para assegurar que todos os requisitos de cada peça no sistema são satisfeitos.

Um exemplo de versionamento utilizado na indústria de software é o versionamento da CELEPAR que também é utilizado em diversos projetos de software livre. A CELEPAR trabalha com algumas regras de versionamento, são eles: Versionamento Principal, Versionamento de segundo nível, Versionamento de terceiro nível, Versionamento por estabilidade e um Versionamento de versões iniciais ou anteriores à versão 1.0. No Versionamento Principal o analista responsável define as versões principais do projeto (v1, v2,...), selecionando quais requisitos serão atendidos em cada versão. No Versionamento de segundo nível é onde se incrementa as versões de segundo nível (v1_0, v1_1,...) quando algum requisito foi alterado ou incluído na versão principal. O Versionamento de terceiro nível (v1_0_1, v1_2_1,...) é utilizado quando a versão de segundo nível foi corrigida devido a algum “bug” encontrado depois da sua disponibilização. O Versionamento por estabilidade é utilizado enquanto uma versão está em desenvolvimento, ou seja, está instável. Assim é utilizada uma versão de segundo nível de número ímpar, por exemplo: v1_1, v1_3. Quando a versão se torna estável ela é promovida para o número par seguinte, por exemplo: se a versão instável era a v1_1, a versão estável deve ser a v1_2. Assim, em produção só poderemos ter versões de segundo nível com números pares. Por fim, o Versionamento de versões iniciais ou anteriores à versão 1.0 é utilizado enquanto a versão inicial de um projeto não fica pronta. Assim é utilizada a versão principal como “v0” e as mesmas regras devem ser aplicadas. As versões de segundo nível devem ser dividas com base na distribuição dos requisitos já alocados para versão 1.0.

Como a CELEPAR utiliza o CVS ela segue uma nomenclatura de versionamento devido limites desse controlador de versão. Assim sendo, cada versão começa com a letra “v” minúscula, seguido da versão principal, por exemplo: v1, v2. O separador utilizado para o segundo e terceiro nível da versão é o “_”, por exemplo: v1_0, v1_0_1, v1_2, v0_5, etc. O nível principal e o segundo são obrigatórios, o terceiro somente se houver.

Como podemos fazer uma boa API

Existe um conceito errado de algumas pessoas que pensam que as APIs são compostas apenas de classes, métodos e o javadoc usado para documentá-los. O javadoc é uma importante ferramenta que fornece uma boa visibilidade da API, porém ainda assim uma API é um termo muito mais amplo do que essa visibilidade poderia indicar. Uma API inclui muitos outros tipos de interfaces.

O exemplo mais óbvio e simples de uma API é um conjunto de classes, seus métodos e seus campos. Provavelmente se estivermos produzindo uma biblioteca em Java iremos empacota-la num arquivo JAR contendo essas classes. Essas classes e seus membros se tornarão uma API da biblioteca.

Nem todas as assinaturas são parte de uma API. Se usarmos classes não públicas, ou privadas ou mesmo campos e métodos privados de pacote, supomos que esses elementos não são parte do contrato que outros programadores deveriam utilizar. Ainda assim esses elementos podem ser usados através de reflexão ou de outras técnicas de baixo nível. Porém, se estivermos precisando de reflexão para nos comunicar com os componentes do sistema, isto indica que algo está errado ou que a API disponível é insuficiente. Isto não quer dizer que reflexão não pode ser utilizada para que possamos ter uma API útil e válida. Pelo contrário, reflexão é muitas vezes usada com sucesso quando precisamos simplificar a codificação e algumas vezes para definir novos padrões de código. Um exemplo disso pode ser encontrado na API JavaBeans onde o usuário de um bean recebe uma lista de métodos e campos que estão disponíveis em tempo de projeto e pode ser modificado através do uso de reflexão. Portanto, reflexão pode ser útil, mas deve ser utilizada com cuidado. Uma forma elegante é documentarmos onde podemos usar reflexão na API.

Outro conceito importante para uma boa API é o conjunto de arquivos que o aplicativo lê ou escreve, bem como seu formato. Se o aplicativo lê um arquivo que se encontra em um determinado lugar e modificarmos esse arquivo de lugar teremos um grande problema, ou então se mudarmos o formato de um arquivo que a aplicação faz uso, também poderemos ter um grande problema e em determinadas situações pode invalidar o funcionamento da API. Algumas aplicações sequer fazem uso do disco rígido, assim sendo não precisam se preocupar com esse tipo de coisa.

Uma situação muito importante de cuidarmos numa API é a estrutura das informações de saída. A implementação padrão do toString() em objetos Java que imprime apenas nomes de classe e códigos objeto de hash hexadecimais não é muito útil. Para fins de depuração, muitas vezes substitui-se os métodos toString() para retornar informações mais significativas. Dessa forma, os objetos sempre devem trazer informações úteis, caso contrário o cliente tentará traduzir as mensagens tentando encontrar algo significativo. Um exemplo disso e que foi concertado na API Java era a antiga Exception.printStackTrace(OutputStream). Não existia uma forma de entender a origem de uma exceção a não ser que pudéssemos traduzir esta saída. Na versão Java 1.4 esse problema foi concertado através de um novo método getStackTrace() que retorna as mesmas informações porém num formato muito mais estruturado. Assim, não foi mais necessário uma análise e um parse na informação textual.

O comportamento de uma API é a parte mais importante do contrato de uma API. Somente se o comportamento de um componente permanece inalterado é que os seus utilizadores podem substituir versões do componente na sua aplicação final. Dessa forma eles podem confiar que a funcionalidade não será comprometida com a atualização para uma versão mais recente. Mantendo o contrato de uma API versão após versão ajuda a tornar a nossa API compatível, ou seja, podemos substituir uma peça do sistema por uma nova versão e o sistema continuará funcionando como era utilizado antes.

Internamente também pode haver incompatibilidades. Por exemplo, se um método retorna um valor não-nulo em uma determinada versão, e então muda para retornar “null” numa outra versão, temos então uma alteração incompatível, pois isso pode influenciar negativamente os componentes do usuário.

As APIs também devem ser destinadas para os mais diferentes grupos de usuários. Um dos casos mais extremos é o suporte a internacionalização, usado por grupos de localização para traduzir módulos individuais em diferentes linguagens. Internacionalização em Java é frequentemente gerenciada utilizando ResourceBundle. Ao invés de mensagens fixas no código define-se uma chave e então é solicitado ao ResourceBundle por um texto naquela chave. O mapeamento entre chaves e mensagens se dá através de arquivos de propriedades como Bundle.properties, Bundle_en.properties, Bundle_ja.properties, e assim por diante. Para grupos de tradução as chaves são muito importantes, assim é importante manter essa compatibilidade entre as diferentes versões procurando evitar remove-los ou renomeá-los.

Como Verificamos a Qualidade de uma API

Alguns desenvolvedores poderiam dizer que uma API é boa se ela é bonita. Ser bonita pode ser uma vantagem, certamente uma boa impressão é sempre válida e tem bastante chance de ser aceita. É por isso que é aceitável que haja um esforço para criar belas APIs, mas a beleza não pode ser a única medida de uma boa API, afinal beleza é algo muito subjetivo e duas pessoas podem não concordar, pois cada um tem suas preferências. A beleza pertence ao mundo da arte, enquanto software é engenharia. O primeiro objetivo da engenharia é produzir sistemas funcionais. Uma API também precisa ser fácil de usar, largamente adotada e produtiva. Uma abordagem de engenharia precisa de uma maneira objetiva de medir a qualidade de seus produtos. Assim, precisamos formular isso para cada API para que possamos medir o grau que um determinado objetivo seja satisfeito.

Nas seções abaixo veremos alguns aspectos visíveis de qualidade para um usuário de uma API, e analisaremos a sua importância e as formas de cumpri-las.

Compreensibilidade

Aqueles que usam uma API devem ser capazes de entendê-la. Uma API é uma comunicação entre o programador que escreve a API e o programador que escreve uma implementação usando esta API. Se ambos não puderem se comunicar, então algo está errado no canal de comunicação entre eles, ou seja, algo está errado na própria API.

Uma atividade bastante similar a escrever uma API é escrever um livro, pois temos um escritor e diversos leitores. Os leitores sabem alguma coisa do escritor, mas o escritor sabe pouco ou nada sobre os seus leitores. Adivinhar suas habilidades e conhecimentos corretamente faz parte da delicada arte de fazer uma API que é fácil de entender.

Os conceitos da API devem estar dentro do horizonte dos usuários da API, ou eles não entenderão. O projetista da API deve entender qual é o conhecimento comum dos seus usuários e usar esse conhecimento quando projetar a API.

Algumas coisas ajudam a mantermos os leitores da nossa API mais familiarizados, como por exemplo, utilizar funcionalidades já conhecidas. Quando codificamos em Java esperamos que as pessoas conheçam conceitos utilizados na maioria das bibliotecas Java como: Iterators, enumerações, I/O, JavaBeans, listeners e componentes visuais. Usar terminologias familiares, classes e termos reduzem a curva de aprendizado o que reduz a demanda cognitiva do usuário da API.

Quando alguma codificação não é familiar ao usuário, a grande maioria dos usuários primeiramente procuram por aplicações existentes que fazem a mesma coisa, dessa forma copiam a aplicação e modificam conforme as suas necessidades. Essa é uma explicação que não importa o quão excepcional seja uma API, possuindo exemplo de como usar aumenta bastante a probabilidade de que os desenvolvedores possam encontrar algo próximo a suas necessidades. Isto também aumenta a probabilidade que eles venham a entender a API.

Consistência

Outro aspecto muito importante de uma API que sempre devemos levar em consideração é se ela é consistente. É sempre importante mantermos o estilo de desenvolvimento ou pelo menos comunicar claramente evoluções no estilo da API. É inconveniente que certas partes da API evoluíram de diferentes formas que as outras.

Descoberta

Mesmo a mais bela API é inútil se não pudermos encontra-la facilmente ou entender como podemos usa-la. Também de nada adianta termos uma API com um javadoc de cinco pacotes e mais de trinta classes sem um ponto de entrada ou orientação. O javadoc do “java.awt.Image” é um exemplo típico desse problema com uma descrição pobre e sem orientação ao usuário de onde ou como carregar uma imagem.

Um ponto importante é que na maioria dos casos, o conjunto de classes disponibilizado pela API não é o que interessa a maioria dos usuários da API. Eles estão interessados em realizar o seu trabalho. Para isso é mais importante para eles ver exemplos de uso da API, e se possível permitindo a seleção do idioma que está mais próximo do que ele quer fazer. Isso explica o sucesso de projetos open source, pois normalmente podemos simplesmente copiar fontes existentes para começar. O código fonte pode servir como documentação e pode fornecer uma orientação inicial.

Dessa forma, é sempre importante criar um lugar único que pode servir como um ponto de início e pode enviar pessoas na direção correta para solucionar seus problemas. Também devemos considerar que as pessoas não pensam em termos de classes, assim é importante organizar este ponto de entrada da melhor forma possível, com base nos objetivos e tarefas reais ou pelo menos esperados pelos usuários da API.

Facilitando Tarefas Simples

Uma API tem diversos tipos de públicos. Algumas vezes uma API que é usada por um tipo de usuário num certo contexto é usado por outro usuário de uma forma completamente diferente. O primeiro erro e o mais básico é colocar itens de interesse para diferentes partes numa API. Isto acaba sendo um problema para a descoberta, visto que as pessoas interessadas em apenas um aspecto da API serão distraídas pelas partes da API que são projetados para um público completamente diferente.

A abordagem mais correta é dividirmos uma API em duas ou mais partes: uma parte é direcionada para os que "chamam" a API, a outra parte de preferência em um pacote ou namespace separado, para aqueles que fornecem pluggings na API para que possam fornecer seus serviços ou extensões. Um exemplo dessa abordagem está na API JNDI em que os projetistas separaram as interfaces para públicos diferentes, separando-a em pacotes completamente diferentes. Os chamadores da API usam “javax.naming” e “javax.naming.event”, enquanto que os implementadores estão mais interessados no “javax.naming.spi”.

Este tipo de separação é mais importante do que documentar a API. Os diferentes públicos da API podem focar seus estudos e conceitos nas suas áreas de interesse.

Preservação do Investimento

É importante tratarmos bem os usuários da nossa API. Quanto mais pessoas escrevem aplicações em Java, mais provável será que outras aplicações em Java serão escritas no futuro. Quando mais desenvolvedores usam a biblioteca JUnit, por exemplo, mais importante a biblioteca e o estilo de desenvolvimento se torna.

Para tornar-se um usuário de uma biblioteca precisamos entende-la e nos convencermos de que ela economiza trabalho. Além disso, os usuários de uma API precisam acreditar que o trabalho deles não será interrompido ou que vai desaparecer quando uma nova versão da API for lançada. Codificar para uma biblioteca é um investimento de tempo, estudo, esforço e dinheiro. Assim, a primeira e principal responsabilidade do projetista da API é preservar o investimento daqueles que a usam.

Por outro lado, também precisamos fazer a API cada vez mais agradável e para isso precisamos renomear métodos para torná-la mais autoexplicativa, reestruturar alguns padrões para a API ser mais compreensível, etc. Essas atividades são sempre bem-vindas antes do lançamento da primeira versão, no entanto, após a primeira versão nem sempre conseguimos antecipar todos os problemas mas sempre devemos levar mais em consideração os problemas relacionados com a quebra de código do cliente da nossa API, situação que deve sempre ser levada em consideração.

Primeira Versão

A primeira versão de uma API nunca é perfeita. Uma boa API não é aquela que parece estar muito boa na primeira versão, mas sim aquela que sobreviveu há vários anos e ainda está em boa forma. Toda API precisa evoluir na medida em que o tempo passa, os requisitos serão alterados com o passar do tempo. Além disso, tem-se que todo programa possui bugs, sendo que esses precisam ser corrigidos e isso por sua vez leva a outra crença comum: corrigir um bug leva a dois novos bugs. Essas observações se aplicam em qualquer sistema de software e APIs não são uma exceção. Também não estamos sendo pessimistas, mas sim realistas. Um ponto importante é que devemos evoluir nossa API, mas sem causar problemas para os usuários da API como já foi discutido anteriormente. Por isso que desenvolvedores de uma API precisam ter um plano de evolução, ou seja, saber a nível estratégico o que vai acontecer com a API em versões futuras.

Existem duas abordagens possíveis para esse plano. Um extremo sugere reescrever tudo do zero, e o outro prefere para tentar corrigir os problemas relatados e melhorar a API existente evitando mudar o comportamento para os clientes existentes.

A abordagem de melhoria incremental pode entregar bugs corrigidos e potencialmente também uma melhor performance e uma melhor aparência, sem qualquer trabalho no lado dos clientes da API. Os clientes ainda podem contar com a velha API e também trabalhar com as versões mais recentes e melhores. Esta seria uma estratégia vencedora, exceto que cada correção de bug é susceptível de introduzir duas novas. Cada nova versão pode apresentar problemas para os clientes da API, razão pela qual alcançar este nível de cooperação pode ser difícil.

Desistir e escrever outra API para executar a mesma tarefa evita o problema de incompatibilidade. A antiga API pode ficar na mesma, assim, não há perigo de ocorrer problemas potenciais para os clientes, e a nova API pode oferecer novas e melhores possibilidades. O único problema aqui é que os clientes antigos vão ficar com a velha API a menos que eles reescrevam seu código e atualizem para uma nova versão da API. Portanto, os inconvenientes também não são triviais. Dessa forma, reescrever completamente uma API tem a vantagem de evitar pequenas incompatibilidades, mas tem a desvantagem de bloquear os clientes para os novos lançamentos que nunca irá se beneficiar das melhorias que uma nova versão daria. As melhorias são importantes, mas a compatibilidade é ainda mais. O equilíbrio entre esses dois extremos é a delicada arte de fazer uma API útil.

Compatibilidade

O primeiro aspecto que enfrentamos no que diz respeito à compatibilidade é a capacidade de compilar um código fonte. Se escrevermos um programa para Java 1.3, será que ele é compatível com Java 1.4? Se isso for possível tem-se que o código é compatível com ambas às versões. Isso é causado devido às adições sintáticas que a linguagem acrescenta. Não faz muito sentido tentarmos garantir a compatibilidade do código fonte em Java, a linguagem em si não é projetada para suportar extensivamente a compatibilidade do fonte. Além disso, tornar o código compatível com diferentes versões do Java não é uma tarefa tão complicada, muitas vezes será necessário apenas alterar alguns “imports”.

Outro tipo de compatibilidade é a compatibilidade binária onde qualquer programa compilado com uma versão anterior de uma biblioteca pode ser ligado com as versões mais recentes da API sem recompilação. Com isso temos dois cenários. Primeiro que podemos escrever uma aplicação e compilar essa aplicação com uma versão da biblioteca, enquanto permitimos que as pessoas usem esta com qualquer outra versão. Isto simplifica a manutenção, empacotamento e distribuição de uma aplicação. O segundo cenário permite que um usuário que tenha apenas o binário, construído com a versão mais antiga da biblioteca, possa migrar para a nova versão da biblioteca, sem precisar esperar por alguém para recompilar o aplicativo. Ambos os cenários são desejáveis e úteis visto que aumentam a flexibilidade de configuração e oferecem uma maior liberdade aos desenvolvedores de módulos e aos usuários. Porém, para alcançar esse nível de interoperabilidade devemos pelo menos entender um pouco dos formatos binários que os fontes são compilados. No caso do Java, isto significa entender o formato do arquivo “class” e como ele é carregado na Java Virtual Machine (JVM).

O último tipo de compatibilidade que podemos ter na nossa API é a compatibilidade funcional. Sabe-se que uma nova versão de uma biblioteca tem compatibilidade de fonte com uma versão anterior se qualquer código que pode ser compilado com a versão anterior pode também ser compilado com a nova versão, ou então ela tem compatibilidade binária se toda aplicação que poderia ser ligada, ainda permanece sendo ligada. Já a biblioteca pode ser compatibilidade funcional se um programa executado na nova versão sempre produz o mesmo resultado como na versão anterior.

API Orientada a Caso de Uso

As pessoas que desenvolvem o Java e as suas bibliotecas principais provavelmente não conhecem todos os seus usuários e nem mesmo as formas que eles usam a API. Mesmo assim os desenvolvedores de APIS que desejam fazer APIs bem sucedidas e amplamente adotadas precisam alcançar uma visão do usuário da API para entender o que deve ser feito com as bibliotecas da API, por que e como fazê-la.

Como nós não conseguimos falar com os clientes que não conhecemos, só temos duas soluções possíveis: ou encontrar os nossos usuários e fazer um estudo de usabilidade, ou ser orientado a caso de uso. Trabalhar com casos de uso é procurar visualizar ações dos usuários utilizando a API e, em seguida, otimizar a API especificamente para estes usuários. Conseguir respostas de usuários por meio de um estudo de usabilidade é bom e podemos verificar que as nossas expectativas sobre os casos de uso estão corretas. De qualquer forma, o ponto de partida para qualquer projeto deve ser uma análise do porquê, o quê e como.

Os casos de uso são por vezes artificiais. Eles podem estar muito longe da realidade e diferem bastante das necessidades reais dos usuários que utilizam a API. Deste ponto de vista, a primeira versão nunca será perfeita. No entanto, essa versão ainda pode ser livre de erros. Um erro no projeto de uma API não é porque API está incompleta. É comum que os novos requisitos estão sendo coletados em todo o ciclo de vida de qualquer sistema, mas a API não pode ser estendida em revisões subsequentes para acomodá-los. Podemos conseguir isto, mesmo sem quebrar o código escrito pelos primeiros usuários das primeiras versões da API.

Um caso de uso é uma descrição do uso a que se destina a API, mostrando o problema que um potencial utilizador pode ter, sem soluções concretas. Obter um conjunto de problemas potenciais o mais próximo possível dos problemas que os nossos usuários possam ter é um passo inicial para a criação de uma solução verdadeiramente útil, que no nosso caso é a API.

Avaliações da API

Todos devem ser responsáveis por participar da elaboração da API e não apenas um arquiteto ou um comitê de pessoas com habilidades em arquitetar sistemas. Devemos incluir até mesmo os desenvolvedores, afinal eles também são desenvolvedores de API de certa forma.

Sempre que quisermos alterar uma API, qualquer um poderia submeter uma solicitação de mudança. Outros então revisam a alteração antes da integração, para nos certificarmos que essa mudança tenha todos os atributos necessários para ser uma boa API. Por exemplo, um revisor poderia verificar nas regras para um projeto de sucesso de uma API se alguns requisitos estão satisfeitos, como:

Um time relativamente pequeno poderia checar esses aspectos do projeto da API. O bom é que não precisamos ter nenhum expert para fazer isso.

Bibliografia

[1] Pressman, R. Engenharia de Software: Uma abordagem Profissional. 7º edição. Editora Bookman.

[2] Jaroslav, T. Practical API Design. Editora Apress.

Artigos relacionados