Tandakan Bendera dengan Musim Bunga

1. Gambaran keseluruhan

Dalam artikel ini, kami akan secara ringkas mendefinisikan bendera ciri dan mengusulkan pendekatan berpandangan dan pragmatik untuk menerapkannya dalam aplikasi Spring Boot. Kemudian, kita akan mencari lelaran yang lebih canggih dengan memanfaatkan pelbagai ciri Spring Boot yang berbeza.

Kami akan membincangkan pelbagai senario yang mungkin memerlukan penanda ciri dan membincangkan kemungkinan penyelesaian. Kami akan melakukannya dengan menggunakan aplikasi contoh Bitcoin Miner.

2. Bendera Ciri

Feature Flags - kadang-kadang dipanggil toggle ciri - adalah mekanisme yang membolehkan kita mengaktifkan atau mematikan fungsi tertentu aplikasi kita tanpa perlu mengubah kod atau, idealnya, menyebarkan semula aplikasi kita.

Bergantung pada dinamika yang diperlukan oleh bendera fitur tertentu, kita mungkin perlu mengkonfigurasinya secara global, per contoh aplikasi, atau lebih terperinci - mungkin per pengguna atau permintaan.

Seperti banyak situasi dalam Kejuruteraan Perisian, penting untuk menggunakan pendekatan paling mudah yang dapat mengatasi masalah yang dihadapi tanpa menambahkan kerumitan yang tidak perlu.

Bendera ciri adalah alat yang kuat yang, apabila digunakan dengan bijak, dapat membawa kehandalan dan kestabilan pada sistem kita. Namun, apabila mereka disalahgunakan atau tidak dijaga, mereka dapat dengan cepat menjadi sumber kerumitan dan sakit kepala.

Terdapat banyak senario di mana bendera ciri dapat berguna:

Pembangunan berasaskan trunk dan ciri-ciri tidak biasa

Dalam pengembangan berdasarkan trunk, terutama ketika kita ingin terus berintegrasi, kita mungkin mendapati diri kita tidak bersedia untuk melepaskan fungsi tertentu. Bendera ciri boleh berguna untuk membolehkan kami terus melepaskan tanpa membuat perubahan kami tersedia sehingga selesai.

Konfigurasi khusus persekitaran

Kami mungkin memerlukan fungsi tertentu untuk menetapkan semula DB kami untuk persekitaran ujian E2E.

Sebagai alternatif, kita mungkin perlu menggunakan konfigurasi keselamatan yang berbeza untuk persekitaran bukan pengeluaran dari yang digunakan dalam persekitaran pengeluaran.

Oleh itu, kita dapat memanfaatkan bendera ciri untuk menukar tetapan yang betul di persekitaran yang tepat.

Ujian A / B

Melepaskan banyak penyelesaian untuk masalah yang sama dan mengukur kesannya adalah teknik menarik yang dapat kita laksanakan menggunakan bendera fitur.

Canary melepaskan

Semasa menggunakan fitur baru, kami mungkin memutuskan untuk melakukannya secara beransur-ansur, dimulai dengan sekelompok kecil pengguna, dan memperluas penggunaannya ketika kami mengesahkan kebenaran perilakunya. Bendera ciri membolehkan kita mencapainya.

Pada bahagian berikut, kami akan cuba memberikan pendekatan praktikal untuk menangani senario yang disebutkan di atas.

Mari kita susun strategi yang berbeza untuk menampilkan penanda, bermula dengan senario paling mudah untuk kemudian beralih ke persediaan yang lebih terperinci dan lebih kompleks.

3. Bendera Ciri Tahap Aplikasi

Sekiranya kita perlu mengatasi salah satu daripada dua kes penggunaan pertama, bendera ciri peringkat aplikasi adalah cara mudah untuk menyelesaikan sesuatu.

Bendera ciri sederhana biasanya melibatkan harta tanah dan beberapa konfigurasi berdasarkan nilai harta itu.

3.1. Ciri-ciri Bendera Menggunakan Profil Musim Semi

Pada musim bunga kita dapat memanfaatkan profil. Dengan mudah, profil membolehkan kita mengkonfigurasi kacang tertentu secara terpilih. Dengan beberapa konstruk di sekitarnya, kita dapat dengan cepat membuat penyelesaian sederhana dan elegan untuk bendera ciri peringkat aplikasi.

Mari berpura-pura kita membina sistem perlombongan BitCoin. Perisian kami sudah dalam pengeluaran, dan kami ditugaskan untuk membuat algoritma perlombongan eksperimen yang diperbaiki.

Di JavaConfig, kami dapat memprofilkan komponen kami:

@Configuration public class ProfiledMiningConfig { @Bean @Profile("!experimental-miner") public BitcoinMiner defaultMiner() { return new DefaultBitcoinMiner(); } @Bean @Profile("experimental-miner") public BitcoinMiner experimentalMiner() { return new ExperimentalBitcoinMiner(); } }

Kemudian, dengan konfigurasi sebelumnya, kita hanya perlu memasukkan profil kita untuk memilih fungsi baru kita. Terdapat banyak cara untuk mengkonfigurasi aplikasi kami secara umum dan mengaktifkan profil khususnya. Begitu juga, ada utiliti ujian untuk menjadikan hidup kita lebih mudah.

Selagi sistem kita cukup sederhana, kita kemudian dapat membuat konfigurasi berdasarkan lingkungan untuk menentukan bendera fitur mana yang akan diterapkan dan mana yang harus diabaikan.

Bayangkan kita mempunyai UI baru berdasarkan kad dan bukannya jadual, bersama dengan pelombong eksperimen sebelumnya.

Kami ingin mengaktifkan kedua-dua ciri di persekitaran penerimaan kami (UAT). Kami dapat membuat fail application-uat.yml :

spring: profiles: include: experimental-miner,ui-cards # More config here

Dengan adanya fail sebelumnya, kita hanya perlu mengaktifkan profil UAT di persekitaran UAT untuk mendapatkan set ciri yang diinginkan.

Penting juga untuk memahami cara memanfaatkan spring.profiles.include. Berbanding dengan spring.profiles.active, yang pertama membolehkan kita memasukkan profil dengan cara tambahan.

Dalam kes kami, kami mahu profil uat juga menyertakan kad eksperimen-penambang dan ui-card .

3.2. Ciri-ciri Bendera Menggunakan Sifat Tersuai

Profil adalah kaedah yang bagus dan mudah untuk menyelesaikan tugas. Walau bagaimanapun, kami mungkin memerlukan profil untuk tujuan lain. Atau mungkin, kita mungkin ingin membina infrastruktur bendera ciri yang lebih tersusun.

Untuk senario ini, sifat khusus mungkin merupakan pilihan yang diinginkan.

Mari tulis semula contoh sebelumnya dengan memanfaatkan @ConditionalOnProperty dan ruang nama kami :

@Configuration public class CustomPropsMiningConfig { @Bean @ConditionalOnProperty( name = "features.miner.experimental", matchIfMissing = true) public BitcoinMiner defaultMiner() { return new DefaultBitcoinMiner(); } @Bean @ConditionalOnProperty( name = "features.miner.experimental") public BitcoinMiner experimentalMiner() { return new ExperimentalBitcoinMiner(); } }

Contoh sebelumnya dibuat di atas konfigurasi bersyarat Spring Boot dan mengkonfigurasi satu komponen atau komponen lain, bergantung pada sama ada harta itu ditetapkan ke benar atau salah (atau dihilangkan sama sekali).

The result is very similar to the one in 3.1, but now, we have our namespace. Having our namespace allows us to create meaningful YAML/properties files:

#[...] Some Spring config features: miner: experimental: true ui: cards: true #[...] Other feature flags

Also, this new setup allows us to prefix our feature flags – in our case, using the features prefix.

It might seem like a small detail, but as our application grows and complexity increases, this simple iteration will help us keep our feature flags under control.

Let's talk about other benefits of this approach.

3.3. Using @ConfigurationProperties

As soon as we get a prefixed set of properties, we can create a POJO decorated with @ConfigurationProperties to get a programmatic handle in our code.

Following our ongoing example:

@Component @ConfigurationProperties(prefix = "features") public class ConfigProperties { private MinerProperties miner; private UIProperties ui; // standard getters and setters public static class MinerProperties { private boolean experimental; // standard getters and setters } public static class UIProperties { private boolean cards; // standard getters and setters } }

By putting our feature flags' state in a cohesive unit, we open up new possibilities, allowing us to easily expose that information to other parts of our system, such as the UI, or to downstream systems.

3.4. Exposing Feature Configuration

Our Bitcoin mining system got a UI upgrade which is not entirely ready yet. For that reason, we decided to feature-flag it. We might have a single-page app using React, Angular, or Vue.

Regardless of the technology, we need to know what features are enabled so that we can render our page accordingly.

Let's create a simple endpoint to serve our configuration so that our UI can query the backend when needed:

@RestController public class FeaturesConfigController { private ConfigProperties properties; // constructor @GetMapping("/feature-flags") public ConfigProperties getProperties() { return properties; } }

There might be more sophisticated ways of serving this information, such as creating custom actuator endpoints. But for the sake of this guide, a controller endpoint feels like good enough a solution.

3.5. Keeping the Camp Clean

Although it might sound obvious, once we've implemented our feature flags thoughtfully, it's equally important to remain disciplined in getting rid of them once they're no longer needed.

Feature flags for the first use case – trunk-based development and non-trivial features – are typically short-lived. This means that we're going to need to make sure that our ConfigProperties, our Java configuration, and our YAML files stay clean and up-to-date.

4. More Granular Feature Flags

Sometimes we find ourselves in more complex scenarios. For A/B testing or canary releases, our previous approach is simply not enough.

To get feature flags at a more granular level, we may need to create our solution. This could involve customizing our user entity to include feature-specific information, or perhaps extending our web framework.

Polluting our users with feature flags might not be an appealing idea for everybody, however, and there are other solutions.

As an alternative, we could take advantage of some built-in tools such as Togglz. This tool adds some complexity but offers a nice out-of-the-box solution and provides first-class integration with Spring Boot.

Togglz supports different activation strategies:

  1. Username: Flags associated with specific users
  2. Gradual rollout: Flags enabled for a percentage of the user base. This is useful for Canary releases, for example, when we want to validate the behavior of our features
  3. Release date: We could schedule flags to be enabled at a certain date and time. This might be useful for a product launch, a coordinated release, or offers and discounts
  4. Client IP: Flagged features based on clients IPs. These might come in handy when applying the specific configuration to specific customers, given they have static IPs
  5. Server IP: In this case, the IP of the server is used to determine whether a feature should be enabled or not. This might be useful for canary releases too, with a slightly different approach than the gradual rollout – like when we want to assess performance impact in our instances
  6. ScriptEngine: We could enable feature flags based on arbitrary scripts. This is arguably the most flexible option
  7. System Properties: We could set certain system properties to determine the state of a feature flag. This would be quite similar to what we achieved with our most straightforward approach

5. Summary

In this article, we had a chance to talk about feature flags. Additionally, we discussed how Spring could help us achieve some of this functionality without adding new libraries.

Kami bermula dengan menentukan bagaimana corak ini dapat membantu kami dengan beberapa kes penggunaan biasa.

Seterusnya, kami membina beberapa penyelesaian mudah dengan menggunakan alat Spring and Spring Boot out-of-the-box. Dengan itu, kami hadir dengan konstruk penanda ciri yang ringkas namun hebat.

Di bawah, kami membandingkan beberapa alternatif. Beralih dari penyelesaian yang lebih sederhana dan kurang fleksibel ke corak yang lebih canggih, walaupun lebih kompleks.

Akhirnya, kami secara ringkas memberikan beberapa panduan untuk membina penyelesaian yang lebih mantap. Ini berguna apabila kita memerlukan tahap butiran yang lebih tinggi.