
Olá pessoal!
Nossa empresa presta serviços de armazenamento e processamento de dados de dispositivos industriais (bombas, brocas e outros equipamentos industriais). Armazenamos os dados de nossos clientes e fornecemos funcionalidade para suas análises: criação de relatórios, gráficos e muito mais.
E, no decorrer do trabalho, percebemos que a integração de cada novo cliente é bastante atrasada e o número de vários erros aumenta constantemente. Então ficou claro que era hora de lidar com isso. Como uma análise da situação mostrou, o departamento de TI de cada um de nossos clientes desenvolveu sua própria solução para coletar dados localmente dos dispositivos e enviá-los para o nosso serviço. Isso complica o fato de que, considerando as especificidades do setor, nem sempre há acesso à Internet e é necessário armazenar dados localmente e enviá-los o mais rápido possível. E há um número suficientemente grande dessas nuances, o que leva a um aumento no número de erros.
E então percebemos que a melhor solução nessa situação seria desenvolver um SDK e fornecê-lo ao cliente. Imediatamente comecei a procurar as melhores práticas e considerações sobre o desenvolvimento do SDK e fiquei muito surpreso - praticamente não há nada sobre isso no RuNet, mas há muito pouca informação na Internet Basurmani e ela está espalhada. Bem, a tarefa é clara, pensada e implementada.
Mas foi a falta de informações sobre esse tópico que criou o desejo de informar a comunidade sobre os pensamentos, decisões tomadas e conclusões sobre o desenvolvimento do SDK. Este artigo discute uma solução para .NET, mas é um conceito, portanto será interessante para muitos. Detalhes sob o corte!
É hora de ser determinado
Vamos começar definindo o que é o SDK e por que ele pode ser necessário.
O SDK (do kit de desenvolvimento de software em inglês) é um conjunto de ferramentas de desenvolvimento que permite que especialistas em software criem aplicativos para um pacote de software específico, software de desenvolvimento básico, plataforma de hardware, sistema de computador, consoles de jogos, sistemas operacionais e outras plataformas. O SDK tira proveito de cada plataforma e reduz o tempo de integração.
...
Um engenheiro de software geralmente recebe um SDK do desenvolvedor do sistema de destino.
Bem, isso é lógico. Em palavras simples, o SDK é um pacote de bibliotecas, para que o cliente possa começar fácil e rapidamente a trabalhar com seu sistema (neste artigo, falaremos sobre nosso serviço, mas tudo descrito no artigo se aplica a outros tipos de SDKs) ou executar as mesmas ações.
Mas, como qualquer abordagem, o SDK Path tem vantagens e desvantagens.
Os benefícios
Integração de alta velocidade de um novo cliente - seus clientes precisam escrever menos código.
Reutilização de código - o mesmo código é usado em vários locais ao mesmo tempo. Podemos dizer que é uma duplicação do parágrafo anterior, mas estamos falando sobre o fato de que a lógica do trabalho está em toda parte solitária, o que implica
Previsibilidade de comportamento - o uso das mesmas bibliotecas eleva o comportamento dos sistemas a um determinado padrão, o que facilita muito a pesquisa e a eliminação de erros e vulnerabilidades.
A qualidade do código é onde muitas pessoas gostam de economizar nos testes (desculpe pelo orçamento, prazos e outros motivos). É claro que, no mundo real, testar todas as seções do projeto com testes é uma tarefa muito trabalhosa. Porém, testar qualitativamente todos os módulos do SDK e usá-los é uma maneira de aumentar a porcentagem de cobertura do teste, o que levará a uma redução no número de erros.
A documentação é o mesmo cenário dos testes. Documentar todo o projeto é bastante problemático. A reutilização de SDKs aumenta a porcentagem de cobertura da documentação, o que reduz o limite de entrada de novos funcionários no projeto e geralmente ajuda na vida.
Todas as vantagens, de fato, são as conseqüências da coisa mais importante - escrevemos o código de altíssima qualidade uma vez e depois o reutilizamos .
Desvantagens
Os requisitos de alta qualidade do código SDK são o resultado da principal vantagem. Um erro no SDK irá gerar erros em todos os sistemas que o utilizam.
Definindo restrições - O SDK é um conjunto de bibliotecas para implementar scripts padrão . Às vezes, os desenvolvedores do SDK acreditam que, além de implementar um dos cenários fornecidos, o cliente não precisa de nada, é mais fácil fazer tudo sozinho do que criar um pedestal de muletas para o SDK.
Inferno e atualizações de dependência - ao expandir a funcionalidade (por exemplo, personalizar uma solução para um cliente específico), você lançará uma nova versão da biblioteca. Porém, existem dependências, conjuntos diferentes de versões de bibliotecas para diferentes clientes e você precisa monitorar cuidadosamente a compatibilidade com versões anteriores ou o controle de versão estrito.
Quando o SDK é realmente necessário
Você tem vários cenários padrão que são implementados novamente de tempos em tempos - na verdade, nosso caso.
Desenvolvimento interno - em diferentes projetos você usa sistemas de log, configurações de sistema, trabalhando com HttpRequest, bancos de dados, arquivos? Crie um SDK interno - um conjunto de bibliotecas para uso interno. Você pode expandir a funcionalidade do SDK a qualquer momento, mas a velocidade do desenvolvimento de novos projetos, a porcentagem de cobertura com testes e documentação aumentará e o limite para a entrada de novos desenvolvedores diminuirá.
Quando é provável que o SDK seja redundante
Os cenários de uso não são definidos ou estão em constante mudança - deixe a implementação de soluções personalizadas para os clientes e ajude-os. Não há necessidade de fazer um prodígio de wunderwaffle, que só irá interferir. Muito relevante para empresas jovens e startups.
Você não sabe como fazê-lo qualitativamente - tenho más notícias para você: é hora de aprender. Mas dar uma decisão errada a um cliente é muito, muito errado. Afinal, os clientes devem ser respeitados.
Então, decidimos o que é o SDK, com suas vantagens e desvantagens, e quando precisamos dele. Se, depois disso, você percebeu que o SDK é realmente necessário - convido você a embarcar no "caminho do SDK" e descobrir o que deveria ser e como diabos fazê-lo?
"Você ama Lego?" - Modularidade
Imagine todos os cenários possíveis para usar o SDK (você já decidiu por que precisa, certo?) E crie um script para a biblioteca. O que não é uma opção? Mas essa é uma abordagem ruim e, portanto, não faremos isso. E seremos assim:
- divida todos os scripts em etapas
- identificar etapas comuns
- crie uma lista de módulos que implementam todas as etapas possíveis (um módulo é responsável por implementar algo específico, por exemplo, trabalhar com configurações)
Por exemplo, levando em consideração as especificidades da tarefa, precisamos que toda a lógica seja definida em configs. Implementamos o módulo para trabalhar com configurações (ler, escrever, atualizar, validar e processar configurações) e o usaremos em todos os outros módulos.
E para a implementação de cenários padrão, realmente criaremos módulos - esses módulos de "controle", cada um dos quais implementa um cenário específico, usando outros módulos do mesmo SDK. Assim, para a implementação de cenários padrão, o cliente precisa apenas conectar o módulo de controle do script (e ele exibirá todas as dependências) e, para a implementação de fora do padrão, usamos os módulos base, também reutilizando o código.
Essa é precisamente a razão pela qual o SDK não deve ser uma biblioteca (embora eu realmente queira, eu entendo. Afinal, quando todo o SDK está em uma biblioteca, você pode esquecer as dependências e tudo o que está conectado a elas), mas ser um conjunto de bibliotecas. Uma vantagem adicional dessa abordagem será a redução no "peso" do programa do cliente - ele puxará um SDK pesado e puxará apenas os módulos necessários.
Mas você não deve produzir módulos de qualquer maneira, porque quanto mais módulos, mais dor de cabeça por suas dependências! I.e. É importante dividir corretamente a lógica em módulos, mantendo um equilíbrio entre a decisão "tudo em um" e "cada módulo tem seu próprio módulo".
"E assim foi possível ?!" - versatilidade
Forneça ao cliente várias interfaces para trabalhar com sua biblioteca. Vou dar um exemplo:
public void LoadConfiguration(string filename); public async Task LoadConfigurationAsync(string filename);
Se você fornecer apenas uma versão síncrona, ao implementar um aplicativo assíncrono, o cliente será forçado a executar invólucros assíncronos do seu método síncrono. Se você fornecer apenas a versão assíncrona, a situação será semelhante. Dê ao cliente ambos e ele agradecerá.
Os genéricos serão uma boa vantagem. Por exemplo, temos uma classe para trabalhar com configurações que implementam métodos para empacotar uma configuração em uma string, carregar uma configuração de um arquivo etc. A configuração de um módulo específico será herdada de nossa classe base, mas, para trabalhar com a nova classe, também precisamos fornecer métodos de desempacotamento.
class BaseConfiguration{ public BaseConfiguration FromString(string source){...} public BaseConfiguration FromString(string source,Type configurationType){...} public T FromString<T>(string source) where T:BaseConfiguration } class CustomConfiguration : BaseConfiguration{}
Assim, fornecemos ao cliente três implementações que ele pode usar. Os genéricos são muito convenientes, mas ao trabalhar com tipos dinâmicos, eles podem ser chamados apenas através da reflexão, o que não é rentável. O princípio geral da universalidade, espero, é claro.
"Pai 1, Pai 2, Filhos []" - Nomeação
Qual é a parte mais difícil em um programador? Invente nomes para variáveis.
No entanto ... A nomeação adequada de módulos, classes, propriedades e métodos ajudará bastante aqueles que trabalharão com seu SDK. Exemplo que não requer comentários:
Exemplo do SDK do Kinect 2.0
var skeletons = new Skeleton[0]; using (var skeletonFrame = e.OpenSkeletonFrame()) { if (skeletonFrame != null) { skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength]; skeletonFrame.CopySkeletonDataTo(skeletons); } } if (skeletons.Length == 0) { return; } var skel = skeletons.FirstOrDefault(x => x.TrackingState == SkeletonTrackingState.Tracked); if (skel == null) { return; } var rightHand = skel.Joints[JointType.WristRight]; XValueRight.Text = rightHand.Position.X.ToString(CultureInfo.InvariantCulture); YValueRight.Text = rightHand.Position.Y.ToString(CultureInfo.InvariantCulture); ZValueRight.Text = rightHand.Position.Z.ToString(CultureInfo.InvariantCulture);
Tudo está claro a partir dos nomes de classes e métodos. E se houver a conclusão do código no seu IDE, muitas vezes é possível não procurar na documentação se tudo já estiver claro.
"Estou certo de que se a Morte soubesse o que era burocracia, as pessoas nunca morreriam, sempre na fila ..." - Documentação
Mas mesmo que todos os módulos, classes, métodos e propriedades sejam nomeados de maneira muito bonita e urgente, você ainda precisará escrever a documentação. Em primeiro lugar, isso poupará bastante os nervos (o número de perguntas dos clientes é reduzido em uma ordem de grandeza. Tudo está na documentação) e, em segundo lugar, sempre fica claro por que você fez isso e não o contrário.
A documentação no SDK é geralmente simples e concisa. Geralmente, é dividido em duas partes: Tutorial - um curso passo a passo no estilo de “Construa uma cidade em 10 minutos” e a seção Referência - uma referência a tudo o que pode ser feito usando este SDK.
Escolhemos a maneira mais fácil - resumo + artigos. Adicionamos atributos XML para métodos e classes que brilham no intellisense como dicas de ferramentas. Usando o Docfx, criamos documentação sobre esses atributos e obtemos documentação detalhada e conveniente, complementada por artigos que descrevem casos de uso e exemplos.
"- Para mantê-lo limpo! - Como vou limpá-lo com um garfo?" - Teste
O que se pode dizer sobre os testes como parte da discussão do SDK ... Precisa ter! A melhor solução seria o TDD (apesar de eu ter uma atitude negativa em relação a essa abordagem, neste caso, decidi usá-la). Sim, há muito tempo. Sim, chato. Mas, no futuro, você não se enforcará com as constantes quedas do SDK do lado e com as conseqüências dessa queda.
O principal motivo da situação é que, ao perder o controle do SDK para o cliente, você não pode corrigir rapidamente o erro, é difícil encontrar esse erro e parecerá estúpido o suficiente em tal situação. Portanto - teste. Teste melhor. E mais uma vez. E, por precaução, teste seus testes. E testar testes. Então, algo que me empolguei, mas espero que a importância de testar o SDK seja clara.
“Uma vítima que não resistiu ao passado foi consumida por ele” - Logi
Como você entrega o SDK a uma empresa terceirizada, como resultado da perda de controle sobre a situação, no caso de um erro (no estágio de teste, todos vocês decidem que "o fará", certo?), Um processo bastante longo e doloroso está aguardando você procurar esse mesmo erro. É aqui que os logs são úteis.
Registre tudo , absolutamente tudo, e se ocorrer um erro, solicite logs ao seu cliente. Dessa forma, você economizará muito tempo e não poderá esfregar o rosto na frente do cliente.
"Alarme! Achtung! Atenção!" - Erros

Embora tenha pensado muito sobre erros, cheguei a uma conclusão interessante - nem um único método no seu SDK deve apresentar um erro que não esteja descrito na documentação . Você deve admitir que é muito desagradável quando você conecta uma biblioteca de terceiros para trabalhar com o HttpRequest e lança alguns NullPointerException e StackTrace em você, o que leva você às entranhas da biblioteca. E você precisa mergulhar nessas "entranhas", tentando entender a profundidade da toca do coelho e qual é o problema.
Portanto, proponho a seguinte solução - declare uma lista fechada de possíveis exceções e documente-as. Mas porque você não pode ter certeza de ter fornecido tudo, agrupar o método em um try-catch e o erro detectado no declarado. Por exemplo, um ConfigurationException que conterá um InnerException é um erro detectado. Isso permitirá que um desenvolvedor de terceiros capture todos os erros possíveis, mas se algo acontecer, descubra rapidamente qual é o problema.
Versões ou "como não morder o rabo"
Para evitar problemas futuros, eu recomendo o uso de versão estrita. Escolha o sistema de versão que mais lhe agrada e use-o. Mas se a nova versão da biblioteca não tiver compatibilidade com versões anteriores, isso deverá ser indicado. Como resolvê-lo - você pensa. Mas você definitivamente deveria pensar sobre isso.
"Um trem que poderia" - implantar
A necessidade de relevância da documentação e das versões gera o requisito para a correção da implantação. Em nossa decisão, usamos a seguinte solução (muletas, mas elas funcionam).
Quando é necessário lançar uma nova versão, o desenvolvedor puxa o bat'nik com o número da versão e, em seguida, o arquivo em lote:
- versão de compilação
- coloca todas as bibliotecas no arquivo
- compile a versão mais recente da documentação (docfx)
- indica a versão do release na documentação e no nome do arquivo
- coloca tudo de mais fresco no repositório git
- O WebApp no MS Azure obtém uma nova confirmação de gancho git e publica alterações
Na saída, temos uma versão atualizada do site com documentação, de onde você pode baixar o arquivo com a versão mais recente do SDK.
Os planos futuros incluem compactar tudo nos pacotes Nuget e publicá-lo no repositório local do Nuget.
Eu recomendo prestar atenção a este ponto, porque você pode reduzir significativamente a quantidade de dor de cabeça causada pela falta de informações relevantes sobre a nova versão da biblioteca.
Um ponto importante na documentação são exemplos de uso. Além disso, muitas vezes é necessário fornecer não uma biblioteca, mas um aplicativo que implemente os cenários mais padrão. Eu recomendo fazer esses aplicativos com código-fonte aberto e bem comentado, o que permitirá que você mate dois coelhos com uma cajadada - forneça um aplicativo funcional e um exemplo de uso do SDK.
Conclusão
O desenvolvimento do SDK tornou-se uma nova tarefa interessante para mim, que levantou muitos problemas importantes de arquitetura. Muitas das coisas descritas no artigo são óbvias (para mim), mas considero importante anunciar até as óbvias para obter uma imagem clara e abrangente.
PS
Obrigado pela leitura, terei prazer em seus comentários. Espero que este artigo seja útil para você.