mobile it



So you want to create a design system, pt. 3: Typography you want to create a design system, pt. 3: Typography<p>Long gone are the days when apps could only use a single system font, bold and italic at most, and that was it. Typography is now a significant part of product identity, but how do you apply it systematically?</p><p>As with colors, the most important thing is to avoid hardcoding type-specific values in the design tool and in the code. This means that you need to define a set of text styles and use them consistently wherever text appears in your UI. </p><p>Both major mobile platforms provide default text styles for different situations such as headings, subheadings, paragraphs, captions, or labels. However, these styles don't match across platforms, and it's also likely that your product needs won't fit neatly into these preset categories. In that case, rather than combining built-in and custom styles, it's easier to define your own styles for everything and ignore the built-in ones.</p><h2>Elements of style</h2><p>So what does the text style contain? To be on the safe side and avoid surprises caused by built-in components and their default values, you should always define at least the following properties:</p><ul><li><strong>Typeface:</strong> The font family you want to use. If appropriate on the platform, it is a good idea to specify a generic (or fall-back) font family too, such as serif, sans-serif, monospace, etc. </li><li><strong>Weight:</strong> Modern font families have a much wider range of weights than just regular and bold, not to mention variable fonts. The weight is usually expressed as a number or a name. Here is a table of the most common values: <img alt="Table of font weights" src="/Blog/PublishingImages/Articles/design-system-3-01.png" data-themekey="#" /> </li><li><strong>Style:</strong> Normal or italics, that's more or less it.</li><li><strong>Width:</strong> The width of each letter. Font families with variable widths are not quite common. Examples include: <img alt="Table of font widths" src="/Blog/PublishingImages/Articles/design-system-3-02.png" data-themekey="#" /> </li><li><strong>Case:</strong> Uppercase, lowercase, or small caps.</li><li><strong>Text decoration:</strong> Overline, underline, or strikethrough text.</li><li><strong>Size:</strong> The height of the characters. This is where it starts to get tricky, see implementation details in the next section. </li><li><strong>Letter spacing (tracking):</strong> The space between the characters. Zero is the default value specified in the font family, but it is often advisable to use a slightly higher positive value to improve readability (especially for paragraph styles with smaller text size), or a slightly negative value to better visually balance large headings. </li><li><strong>Line height:</strong> Vertical space between text lines, measured from baseline to baseline. Baseline is the invisible line on which each character sits, not including downstrokes (like in lowercase letters <em>p</em> or <em>g</em>).<br>As with letter spacing, each font family has a default value that may be adjusted for readability. </li><li><strong>Paragraph spacing:</strong> Vertical space between paragraphs.</li><li><strong>Paragraph alignment:</strong> Left, right, center, or block. Be careful with block alignment, as the legibility and visual quality of the resulting typesetting depends a lot on the quality of the algorithm used (which usually cannot be changed), including hyphenation algorithms for different languages. </li><li><strong>Text direction:</strong> If your application supports languages that are written from right to left, you often need to adjust layouts as well, and consistently use layout terms that are independent of text direction, such as start and end instead of left and right. </li><li><strong>Color:</strong> Should color be directly part of the text style specification? A slightly tricky question, both options have their pros and cons. However, specifying a default color probably won't do any harm, so I’d generally recommend including color in text style specification. </li></ul><h2>Practical type system</h2><p>So what text styles does a typical application need?</p><p>First of all, it is a good idea to distinguish between text styles for a text that stands on its own, as “top-level” content (headings, paragraphs, labels, captions, notes, etc.), and text styles for components that happen to contain text (buttons, menus, toolbars, input fields, tabs, etc.).</p><p>Some very well-known design systems don't distinguish between those usages (or, on the contrary, mix those together), but this is unfortunate—it often happens that in such systems a change of paragraph style unintentionally results in a change of the text style in some component like button or input field, which is something you usually don't want.</p><h2>Content text styles</h2><p>As with colors, it's a good idea to hide content text styles behind semantic names. The choice is completely yours, but usually, you will need at least several levels of headings and subheadings, one or two styles for regular text in paragraphs, accompanying styles such as captions or notes, and maybe even some styles for things like list items, etc.</p><p>If your app's domain is so specific that it's worth creating styles for concrete elements (e.g. cart items in an e-shop app, or waypoints in a navigation app), then definitely do so, even if those styles are visually very similar or even the same as the general-purpose styles. It's important to be able to change text styles that have common semantics (which means they change together, for the same reason), not just a coincidentally common look. </p> <img alt="Content text styles example" src="/Blog/PublishingImages/Articles/design-system-3-03.png" data-themekey="#" /> <h2>Component text styles</h2><p>What about component text styles? Most importantly, they should be considered private implementation details of the components, meaning they mustn’t be used in other components or stand-alone text.</p><p>They can only be reused between a group of tightly knit components, e.g., it’s fine to have a common text style for primary, secondary, outlined, or text buttons, but it’s a bad idea to share this style with unrelated components like tabs or chips—chances are, some of these components will change independently (at a different time, or for a different reason, or both), causing problems in unrelated places.</p><p>Beware—many platforms support some kind of inheritance for text styles, meaning you can derive a new style from an existing one by adding or overriding properties. Although this feature looks appealing because it can save implementation effort, when used incorrectly it leads to unwanted coupling, similar to the reuse of styles in unrelated components.</p><p>Never misuse inheritance as a tool to share implementation. Inheritance only works when it creates an “is-a” relationship—e.g., a secondary button certainly is a kind of button, but a tab is probably not a kind of button, and thus its text style should be kept separate.</p> <img alt="Component text styles example" src="/Blog/PublishingImages/Articles/design-system-3-04.png" data-themekey="#" /> <h2>Technical difficulties</h2><p>You may often encounter some complications during text style specification and implementation:</p><ul><li>Size units are a minefield. There are a large number of units and each platform uses its own specific ones. Sometimes the platform may even use different units for different things, which can be further complicated when the platform must support displays with different physical resolutions.<br>The system design specification needs to state the values using the units appropriate for each platform, or at least provide a conversion formula. </li><li>Be careful, not all fonts provide all the weights or styles. Some platforms then try to interpolate the weight when asked for a value that the font does not contain, and the result is usually obviously fake, and visually pretty bad; the same can happen with italics. <img alt="Real and fake italics and bold" src="/Blog/PublishingImages/Articles/design-system-3-05.png" data-themekey="#" /> </li><li>Since the text in the application can come from a variety of sources, and some have built-in formatting (e.g. when displaying HTML, Markdown, etc. with bold and italics applied), the formatting may interfere with the specified weight or text style. In this case, you need to either remove the formatting first, or specify what bold and italics actually mean for each text style. </li><li>You need to be absolutely sure that the font family you choose contains all the characters from all the languages your application supports. It's not just a problem of seeing embarrassing "tofu" blocks in place of missing glyphs, but also of rendering diacritics correctly. <img alt="Missing and invalid glyphs" src="/Blog/PublishingImages/Articles/design-system-3-06.png" data-themekey="#" /> </li><li>Especially if your application uses multiple font families, each with multiple weights and styles, pay attention to size of the font files you are packaging with the application. Higher download sizes increase customer churn. </li></ul><p>Other problems you may encounter include:</p><ul><li><a href="">Languages vary greatly in word length.</a> You have to take this into account especially when designing layouts, because what fits on one line in one language may need two or more lines in another. You have to define what should happen in that case, e.g., words are replaced, ellipsized, hyphenated, text size is reduced… </li><li>Especially if you are designing mobile apps, you necessarily need to see all the text on an actual phone screen. Checking on a computer or laptop display is not enough, because it may give you a distorted impression of the size and, above all, the readability of the text.<br>Also remember that many people still use mobile devices with lower physical resolution, which has a big impact on readability, especially the text is small. It may often be appropriate to use other font families that render better on such devices. <img alt="Displays with different resolutions" src="/Blog/PublishingImages/Articles/design-system-3-07.png" data-themekey="#" /> </li></ul><h2>Headline2</h2><p>Typography plays a crucial aesthetic and practical role in a design system. At the same time, it is important to create a robust yet flexible set of text styles to support different functions of text in digital products.</p><p>Next time we'll look at one more important part of design systems—the grid.</p>#design-system;#ui;#ux;#development;#android;#iOS
Our first year with Kotlin Multiplatform first year with Kotlin Multiplatform<p>Kotlin Multiplatform allows you to use the same programming language on mobile, desktop, web, backend, IoT devices, and more. There is a large number of possibilities and a steadily growing number of real-world applications.</p><p>Kotlin Multiplatform was introduced at the end of 2018 and many teams have started adopting it right away, first only on smaller parts of their projects, which later grew over time.</p><p>In the case of the Cleverlance mobile team, we took a different approach in adopting Multiplatform technology. From the beginning, we believed Kotlin Multiplatform was the right approach to share business and application logic between Android and iOS mobile platforms. We've been following cross-platform and multi-platform technologies for a long time, but prior to the advent of Kotlin Multiplatform, none had convinced us of their long-term sustainability in an app agency environment like ours. It's not just about the functionality of the technology itself. What is important is the maturity of the whole platform, the community around it, and last but not least the good availability of experts in the subject matter, as my colleague summarized in his article <a href="/Blog/Pages/choosing-mobile-app-technology.aspx">Questions to ask before choosing mobile app technology</a>.</p><h2>(Too) early days</h2><p>As with any new technology we consider for use in our production applications, we set up a small project with Kotlin Multiplatform and tried to implement elementary tasks like sharing pure Kotlin code between Android and iOS, sharing network services, database access, or offloading work to a background thread.</p><p>At the same time, we started testing the right application architecture and establishing which parts of the application are suitable to share between platforms and how to do it properly. We addressed questions about the structure and rules for building shared code API, whether it's possible to call Kotlin Coroutines from Swift code, etc. And last but not least, we tested the suitable structure of the project and created a build pipeline, at the end of which an Android and iOS app package will be created.</p><p>In the beginning, the work went rather slowly. We built a list of problems or unresolved issues that prevented us from using the technology in our production apps. However, Kotlin Multiplatform has evolved very dynamically and we have to really appreciate the response time of its authors when bugs or shortcomings reported by us were resolved and a new version released in a matter of weeks.</p><p>During 2020, our demo project was gradually becoming usable, the list of unresolved issues was getting shorter, and we were eagerly awaiting the stable release of Kotlin 1.4, which promised a lot of good things.</p><h2>Starting off</h2><p>This happened at the end of summer 2020 when the list of issues that would prevent the Kotlin Multiplatform from being used in production was down to the last two.</p><p>The first one concerned <a href="">how to optimally build Kotlin code as a framework</a> for iOS apps. Even today, a year later, this topic is still not completely resolved, but the solution in the form of the so-called <a href="">Umbrella module (or Integration module)</a> turned out to be functional and sufficient for our needs, without limiting our work in any way.</p><p>The second one is about <a href="">the memory model in Kotlin/Native</a> (in our case for iOS), and the possibility of using a background thread for coroutines. This one doesn't have a final implementation yet either, but a final solution <a href="">is on the way</a>. However, a temporary solution from JetBrains in the form of a special <a href="">native-mt Coroutines</a> build has proven to be sufficient in not delaying the use of Kotlin Multiplatform any further.</p><h2>First app</h2><p>On 1st September we started working on a new project. It was a mobile application for the consumer/marketing apps sector, for Android and iOS. During the first week of the project, we worked in a team of 1 Android and 1 iOS engineer on the basics of the project structure and app architecture, but especially on the alignment of practices for creating interfaces between shared and platform code and between Kotlin and Swift. Both of us were already experienced in Kotlin Multiplatform, which was important especially on the part of the iOS developer, who was thus already familiar with Kotlin and well aware of the platform differences.</p><p>At Cleverlance, we are proponents of Clean Architecture and have applied its principles to native app development for years. In terms of structure and architecture, we didn't need to invent any new approach, we just adapted our habits and proven practices in a few places to share code between the two platforms more efficiently.</p><h2>Worlds collide</h2><p>In the next phase of the project we added one more Android and iOS engineer, but they hadn't worked with Kotlin Multiplatform yet, so it was interesting to see how quickly they would get comfortable with the new mindset.</p><p>Perhaps unsurprisingly, this was not a major problem for the Android engineer. Due to the almost identical architecture and familiar language, it was enough to get used to the slightly different project structure (especially the module structure, the separation of shared and platform code, etc.) and to the new technologies, e.g. for network communication or storage, where it is not possible to directly use the well-known platform libraries, but one has to reach for the multiplatform ones (although these often just cover those known platform technologies under a single multi-platform roof).</p><p>It was much more interesting to watch the progress of the iOS developer who had never written a single line of Kotlin and had never encountered the Android world or the structure of Gradle projects. But as he was no stranger to the architecture, even though up until this point he had only known it in similar iOS projects written in Swift. It turned out that those principles and practices we shared years before between Android and iOS developers are that crucial foundation, and the technologies or programming languages are just the tools by which they are implemented.</p><p>So at first, this iOS engineer worked mainly on the iOS part of the app, and only occasionally modified shared code written in Kotlin. But he very quickly started writing more of the shared code on his own, until he opened his first PR at the end of September, in which a small but complete feature was implemented in the shared code and the iOS platform implementation. I, as an Android developer, then only wrote the small platform part for Android, on which I spent about 2 hours instead of the 2 days that the feature would have taken me if I had written it all natively for Android.</p><p>And let me tell you, it feels nice and exalting. Then, when that same iOS developer single-handedly fixed a bug in the Android app for us a few weeks later, I realized that this method of development has a whole new dimension that we never dreamed of before.</p><p>The following weeks and months of the project weren't entirely without problems. We occasionally ran into inconsistencies between the nascent Kotlin Multiplatform libraries, especially between Kotlin Coroutines and Ktor. The iOS framework build system stumbled a few times, but none of these issues ever gave us even a hint of stopping the development, and what's more, problems of this type gradually subsided. Around December 2020, after a new version of Kotlin Coroutines 1.4 was released, fully in line with the principles of multiplatform development, these difficulties became completely marginal and we were able to concentrate fully on the app development.</p><h2>Crunching the numbers</h2><p>As the project entered its final phase just before its release into production, it was time to look at the numbers.</p><p>When I checked the last two projects that we created as standard separate native Android and iOS apps, I found that the amount of code (lines of code) required for the Android and iOS app was pretty similar. In fact, the iOS apps were slightly smaller, but I attribute that mostly to the chatty way UI definitions are done on Android, a thing that is changing dramatically with the advent of technologies like Jetpack Compose and Swift UI. Likewise, at the project level, it can be argued that a similar amount of time is required to implement apps for both platforms.</p><p>As for our first multiplatform project, it worked out as follows in terms of lines of code:</p> <img alt="Comparison of lines of code" src="/Blog/PublishingImages/Articles/kotlin-multiplatform-first-year-1.png" data-themekey="#" /> <p>If the effort to implement one native app is 100%, then with Kotlin Multiplatform almost 60% of the code can be shared, and only a little over 40% needs to be implemented twice, for both Android and iOS, meaning the effort to implement both apps is 140% instead of 200%, saving almost a third of total development costs. Here again, it turns out that the amount of code needed for finalization on both platforms is similar. It should be noted that we also count a non-negligible amount of unit tests that we write only once and share.</p><p>The actual breakdown of which parts of the code we share and which we don't is a topic for a separate post, but as a rough preview, I'd give the following chart:</p> <img alt="Amount of shared code per area" src="/Blog/PublishingImages/Articles/kotlin-multiplatform-first-year-2.png" data-themekey="#" /> <p>The user interface is a very platform-specific layer, but the presentation layer does not contain such differences, and the reasons we did not share it in the first project are more technical. On subsequent multiplatform projects, we have however focused more on this part and now we are able to share the presentation layer code at about 70%, which has a positive impact on the percentage of overall code shared in the project.</p><p>When we look at the amount of time spent on this project, we get this graph:</p> <img alt="Comparison of time spent per developer" src="/Blog/PublishingImages/Articles/kotlin-multiplatform-first-year-3.png" data-themekey="#" /> <p>However, the dominance of reported Android developer time is not due to Android apps being more demanding, but simply because Android developers had more time to spend on the project. In fact, one of the iOS developers was not always able to devote 100% of his time during the project, but this did not affect the speed of development for both apps, as his time was simply compensated by the Android developers. The same worked the other way around when, for example, both Android developers were on holiday at the same time. This is not to say that an Android and iOS developer is an equivalent entity in terms of project staffing, but definitely, the multiplatform development gives you a certain amount of flexibility in human resource planning.</p><h2>Unexpected perks</h2><p>At the end of this post, I'd like to mention a few interesting facts and side effects we noticed during the development:</p><ul><li>The project is not fundamentally more demanding in its setup than a standard single platform project. Creating the basis for a new project takes a similar amount of time, in the order of days. </li><li>It's very good if Android developers learn at least the basics of Swift and working in Xcode. They can better prepare the shared code API and make small adjustments atomically across both platforms and shared code. </li><li>For iOS developers, learning Android technologies and ecosystem often involves discovering better tools than they are used to, which motivates them in their endeavors. </li><li>The second usage of shared code works as a very careful code review, and for some teams, this will allow the standard code review done during pull requests to be either reduced or removed entirely, thus increasing the development momentum. </li><li>From a project and business management perspective, we are building just one application. The same is true when communicating with the backend, where both applications act as one, there are no differences in implementation on each platform and it greatly facilitates team communication. </li><li>Short-term planned, but also unplanned developer downtime does not affect team velocity.</li><li>On one of the following projects, we were able to work in a mode of 1.5 Android developers and 3.5 iOS developers, with the development of both apps progressing similarly. </li></ul><h2>Conclusion</h2><p>It's been more than a year since we started working on our first application using Kotlin Multiplatform, and as the text above indicates, it hasn't remained an isolated experiment.</p><p>We are currently using this technology on five brand-new application projects. Besides that, we are discussing the opportunities with several long-term customers to deploy this technology in existing projects.</p><p>Kotlin Multiplatform is maturing like wine and we look forward to bringing the mobile platforms even closer together.</p><p><br></p><p>Pavel Švéda<br></p><p>Twitter: <a href="">@xsveda</a><br><br></p>​<br>#kotlin;#android;#iOS;#multiplatform;#kmp;#kmm
Underused Kotlin features Kotlin features<p>Kotlin is a modern and rapidly evolving language. Let's explore some nooks and crannies to see if there are any hidden gems.</p><h2>Value classes</h2><p>We often see domain models that look like this:</p><pre><code class="kotlin hljs">// DON'T DO THIS! data class CarCharger( val id: Long, val distance: Int, val power: Int, val latitude: Double, val longitude: Double, val note: String? = null /* ... */ )</code></pre><p>Unfortunately, this antipattern is so widespread that it has earned its own name - <a href="">primitive obsession</a>. If you think about it, it turns out that in the domain classes that we model based on real business entities, actually, very few things are <em>unbounded</em> Ints, Doubles, or Strings with totally arbitrary content.</p><p>The solution is to replace these primitives with proper, well-behaved types - wrapper classes that prohibit invalid values and invalid assignments:</p><pre><code class="kotlin hljs">// DON'T DO THIS! data class Latitude(val value: Double) { init { require(value in -90.0..90.0) { "Latitude must be in range [-90, 90], but was: $value" } } }</code></pre><p>We can use this class in the <span class="pre-inline">CarCharger</span> instead of the primitive type. This is much better in terms of safety and code expressiveness, but unfortunately it also often results in a noticeable performance hit, especially if the wrapped type is primitive. </p><p>But fret not! It turns out that thanks to Kotlin’s value classes, you can have your cake and eat it too! If you slightly modify the class declaration:</p><p></p><pre><code class="kotlin hljs">@JvmInline value class Latitude(val value: Double) { init { require(value in -90.0..90.0) { "Latitude must be in range [-90, 90], but was: $value" } } }</code></pre><p>the compiler (similarly to what happens with inline functions) will replace the class with the wrapped value at each call site. Thus, at compile-time, we have all the benefits of a separate type, but no overhead at runtime. Win-win! <a href="">Inline classes also make great database IDs</a>. </p><p>Similar to data classes, <span class="pre-inline">equals</span> and <span class="pre-inline">hashcode</span> are automatically implemented for value classes based on the wrapped value (because value classes have no identity). Value classes can also have many features of standard classes, such as additional properties (without backing fields), or member functions, but there are also some restrictions - they cannot inherit from other classes (they can however implement interfaces), and they must be final. </p><p>Be sure to read the <a href="">full documentation</a> and how value classes in Kotlin relate to <a href="">Java’s upcoming Project Valhalla</a>.</p><p>With consistent use of value classes, your domain models (and other code, of course) can be significantly more secure and readable:</p><pre><code class="kotlin hljs">data class CarCharger( val id: CarChargerId, val distance: Kilometers, val power: Kilowatts, val coordinates: Coordinates, val note: String? = null /* ... */ )</code></pre><h2>Computed properties</h2><p>Computed properties are properties with custom getter and setter but without a backing field. They can be used to locally "overload" the assignment "operator".</p><p>For example, the currently popular reincarnation of the MVVM pattern involves a view model with a public, asynchronous, observable stream of UI states that are continuously rendered by the view. In Kotlin this state stream can be represented by <a href="">Flow</a>, or more appropriately by its subtype <a href="">StateFlow</a>, which has some properties more suitable for this situation:</p><pre><code class="kotlin hljs">interface ViewModel<S : Any> { val states: StateFlow<S> }</code></pre><p>Let's create an abstract base class that will serve as the basis for concrete implementations. In order for the view model to update states, it must internally hold a writable version of StateFlow:</p><pre><code class="kotlin hljs">abstract class AbstractViewModel<S : Any>(defaultState: S) : ViewModel<S> { protected val mutableStates = MutableStateFlow(defaultState) override val states = mutableStates.asStateFlow() }</code></pre><p>If a concrete view model subclass wants to emit a state update based on the previous state, it must do something like this:</p><pre><code class="kotlin hljs">class GreetingViewModel : AbstractViewModel<GreetingViewModel.State>(State()) { data class State( val greeting: String? = null /* other state fields */ ) fun onNameUpdated(name: String) { mutableStates.value = mutableStates.value.copy(greeting = "Hello, $name") } }</code></pre><p>This works, but the code isn't quite readable, and worse, the implementation details of the abstract view model (that it uses <span class="pre-inline">MutableStateFlow</span> internally) leak into the concrete view model - classes must be well encapsulated not only against the outside world but also against their subclasses! </p><p>Let's fix this by hiding the <span class="pre-inline">MutableStateFlow</span> in the base view model, and instead provide a better abstraction for subclasses:</p><pre><code class="kotlin hljs">abstract class AbstractViewModel<S : Any>(defaultState: S) : ViewModel<S> { private val mutableStates = MutableStateFlow(defaultState) override val states = mutableStates.asStateFlow() protected var state: S get() = mutableStates.value set(value) { mutableStates.value = value } }</code></pre><p>The function in the subclass that needs to update the state then can look like this:</p><pre><code class="kotlin hljs">fun onNameUpdated(name: String) { state = state.copy(greeting = "Hello, $name") }</code></pre><p>The subclass now has no idea how the states are implemented - from its point of view, it just writes and reads the state from a simple property, so if in the future the mechanism for emitting states needs to be changed (and this has happened several times during the development of Kotlin coroutines), individual subclasses will not be affected at all.</p><p> <em>Note: The above code is theoretically thread-unsafe, but depending on the context (view model running on the main thread) this may not be an issue.</em></p><h2>Pseudoconstructors</h2><p>In all non-trivial systems, it is important to abstract the object creation process. Although Kotlin must ultimately call a constructor <em>somewhere</em> to create a new instance, this doesn’t mean that all code should be <em>directly coupled</em> to these constructors - quite the opposite. A robust system is independent of how its objects are created, composed, and represented.</p><p>Many <a href="">classic design patterns</a> were created for this purpose, and many of them are still valid with Kotlin, but thanks to the interplay of Kotlin features, we can implement some of them with a twist that improves the discoverability and readability of the resulting code.</p><p>When exploring an unfamiliar API, I would argue that the most intuitive way and the first choice to create an object based on its type is to call its constructor.</p><p>Creational patterns, however, are meant to abstract concrete constructors away, so in traditional languages we may instead see constructs such as:</p><pre><code class="kotlin hljs">Foo.createFoo() Foo.getInstance() Foo.INSTANCE FooFactory.create() Foo.Builder().build() FooBuilder.getInstance().build()</code></pre><p>There are many possibilities and combinations, and it can be challenging to keep them all in your head. Luckily, Kotlin can help us!</p><p>The first example is a basic factory. Let's say we have a point of interest interface called simply <span class="pre-inline">Poi</span>. There are many specific types of POIs with different properties and we need a factory to instantiate them from their serialized representation. </p><p>If our factory can be stateless, we can simply create a top-level function of the same name in Kotlin:</p><pre><code class="kotlin hljs">fun Poi(serialized: String): Poi</code></pre><p>The call site then (except for an import statement maybe) looks exactly the same as if we were calling the constructor.</p><p>Moreover, we can do things with top-level functions that we can't do with constructors - for example, we can have such functions in different modules with different visibility and parameters, for different purposes, in different layers, etc., while a constructor always has to live in its own class.</p><p>This way we can also create "extension constructors" for types we don't own, for example:</p><pre><code class="kotlin hljs">fun ByteArray(base64: String): ByteArray { /* ... */ }</code></pre><p>And if our factory function has default parameters, it can also replace simpler builders.</p><p>Coroutines library authors do something similar with Jobs. When you write</p><pre><code class="kotlin hljs">val job = Job()</code></pre><p>what you actually call is this function:</p><pre><code class="kotlin hljs">fun Job(parent: Job? = null): CompletableJob = JobImpl(parent)</code></pre><p>Here, a "constructor" of a known type actually returns its public subtype, implemented by a private subclass. This gives the library authors a great deal of flexibility for the future.</p><p>The second example is more complicated - let's say we've been tasked with creating a year view for a calendar application where workers in a factory can see their shift schedule and other necessary data. The UI looks something like this:</p> <img alt="Calendar UI" src="/Blog/PublishingImages/Articles/underused-kotlin-features-01.png" data-themekey="#" /> <p>and the domain model of one day is as follows:</p><pre><code class="kotlin hljs">data class Day( val date: LocalDateTime, val shift: Shift, val dayType: DayType, val workType: WorkType ) { enum class Shift { None, Day, Night } enum class DayType { Normal, Weekend, NationalHoliday } enum class WorkType { Normal, Inventory, Maintenance, Training, Vacation } }</code></pre><p>Since the calendar can display several years at once in this view, and there are many possible combinations in each cell, and there can be hundreds of cells on the screen at once, and the whole thing has to scroll smoothly both vertically and horizontally, it is not possible for performance reasons to implement individual cells as regular widgets with an image and a text field.</p><p>We need to optimize this UI so that the individual cells are bitmaps that we render directly to the screen. But there would still be hundreds of such bitmaps, and color bitmaps take up a surprising amount of memory surprisingly quickly.</p><p>The solution is to cache bitmaps that look the same, effectively making them <a href="">flyweights</a>. This will save a significant amount of rendering time and memory.</p><p>In a classic design, we would create a <span class="pre-inline">BitmapFactory</span>, add some <span class="pre-inline">BitmapCache</span>, and somehow wire it all together. With Kotlin, we can do this:</p><pre><code class="kotlin hljs">class DayBitmap private constructor(val imageBytes: ByteArray) { /* other properties and methods */ companion object { private val cache = mutableMapOf<DayCacheKey, DayBitmap>() private fun Day.cacheKey(): DayCacheKey = ... private fun Day.render(): ByteArray = ... operator fun invoke(day: Day): DayBitmap = cache.getOrPut(day.cacheKey()) { DayBitmap(day.render()) } } }</code></pre><p> <span class="pre-inline">ImageBytes</span> are raw image data that can be directly rendered to the screen. <span class="pre-inline">Cache</span> is a "static" global private cache for unique rendered images of days, <span class="pre-inline">DayCacheKey</span> is a helper type serving as a key to this cache (<span class="pre-inline">Day</span> class cannot be used as a key because it contains a date that is unique for each day - so <span class="pre-inline">DayCacheKey</span> uses all the fields from Day <em>except</em> the date).</p><p>The main trick is however the <span class="pre-inline">invoke</span> operator added to the <span class="pre-inline">DayBitmap</span> companion object.</p><p>First of all, what happens inside: A cache key is created from the given day, and if we already have a <span class="pre-inline">DayBitmap</span> object saved in the cache for this key, we return it immediately. Otherwise, we create it on-demand using its private constructor (which no one else can call!), cache it, and return it immediately. This is the actual flyweight-style optimization. </p><p>But the greatest beauty of this approach is in the creation of <span class="pre-inline">DayBitmaps</span>. The long version of the call is this:</p><pre> <code class="kotlin hljs">// DON’T DO THIS! DayBitmap.Companion.invoke(day)</code></pre><p>But since we don't have to explicitly state a companion with an implicit name, and the <span class="pre-inline">invoke</span> operator just looks like parentheses in a function call, we can shorten the whole thing, and the call-site usage is then indistinguishable from a constructor call, for example</p><pre> <code class="kotlin hljs">val bitmaps = { day -> DayBitmap(day) }</code></pre><p>but with the huge difference that this transformation is <em>internally</em> optimized!</p><h2>More than the sum of the parts</h2><p>The charm of Kotlin is often how its individual features can often be used together in somewhat unexpected ways. This was just a small sampling of the less frequent abilities Kotlin has to offer - we'll look at some more next time.</p>#kotlin;#development;#android
Scrum smells, pt. 7: Wishful plans smells, pt. 7: Wishful plans<p>​​​​ In the preceding parts of the planning series, we were just preparing our ground. So today, let's put that into practical use and make some qualified predictions. </p><p> You're planning an initial release of a product and you know what features need to be included so that it gets the necessary acceptance of users. Or your stakeholders are asking you how long it will take to get to a certain feature. Or you have a certain budget for a project and you're trying to figure out how much of the backlog is the team capable of delivering for that amount of money. </p><h2>Measuring velocity</h2><p> There is a useful metric commonly used in the agile world called development velocity (or team velocity). It basically says, what is the amount of work that a particular team can do within one sprint on a certain product in a certain environment? </p><p> In essence, it's just a simple sum of all the work that the team is able to do during a sprint. It is important to count only the work that actually got to the state where it meets the definition of done within that particular sprint. So when a team does work worth 50 story points within a sprint, that's the team's velocity in that given sprint. </p><p> Nonetheless, we must expect that there are variables influencing the “final” number. Estimates are not precise, the team might have its members sick or on vacation and so on. That means that the sprint velocity will vary between the sprints. So as always, the longer we observe and gather data, the more reliable numbers we can get. Longer-term statistical predictions are usually more precise than short-term ones. </p><p> So over time, we can calculate averages. I found it useful to calculate rolling averages over several past sprints because the velocity usually evolves. It smooths out local dips or highs caused for instance by the parallel vacation of several team members. Numbers from the beginning of a project will probably not relate very much to values after two years of the team maturing. The team gets more efficient, makes better estimates, and also the benchmark for estimates usually changes somewhat over the course of time. </p><p> That means that we will get an average velocity that represents the typical amount of work that a given team is able to do within one sprint. For instance, a team that finished 40, 65, 55, 60, 45, and 50 story points in subsequent sprints will have an average velocity of slightly over 50 story points per sprint over that time period. </p><p> Note: If you're a true geek, you can calculate standard deviation and plot a chart out of it. That will give you a probability model. </p><h2>Unexpected work's ratio</h2><p> Now the last factor we need to know in order to be able to create meaningful longer-term plans is the bias between the known and unknown work. </p><p> I'll use an example to explain the logic that follows. So let's say we have 10 user stories at the top of our product backlog, worth 200 story points. The development team works on them and after 4 sprints it gets them done. But when retrospectively examining the work that was actually done within those past 4 sprint backlogs, we see that there was a lot of other (unpredicted) stuff done apart from those original 20 stories. If we've been consistent enough and have most of the stuff labeled with sizes, we can now see their total size. Let's say 15 unexpected items got done in a total size of 75 story points. </p><p> That means we now have an additional metric. We can compare the amount of unexpected work to the work expected in the product backlog. In this particular example, our ratio for the past 4 sprints is 75:200, which means that for every expected story point of work, there came almost 0,4 additional story points that we had not known about 4 sprints ago. </p><p> Again, this evolves over time and you also get more precise numbers as time passes and the team matures. On one of our projects, we came to a long-term statistic of 0,75 of extra story points of unpredictable stuff for every 1 known story point, just to give you some perspective. </p><p> Having a measurable metric like this also helps when talking to the stakeholders. No one likes to hear that you keep a large buffer just in case; that's hard to grasp and managers usually will try to get rid of that in any planning. So a metric derived from experience is much easier to explain and defend. </p><h2>Making predictions</h2><p> So back to the reason why we actually started with all these statistics in the first place. In order to provide some qualified predictions, we need to do some final math. </p><p> With considerable consistency, we got to a state where we know the (rough) sizes of items in our backlog and therefore we know the amount of known work. Now we also know the typical portion of the unexpected stuff as a ratio to the known work. You also know the velocity of your team. </p><p> We will now add the percentage of unpredicted work to the known work and we get the actual amount of work that we can expect. Dividing by the team's velocity, we can get to the amount of time the team will need to develop all of it. </p><p> Let's demonstrate that with an example: There's a long list of items in the product backlog and you're interested in knowing how long it will take to develop the top 30 of them. There shouldn't be any stories labeled with the “no idea” sizes like “100” or “??”. That would skew the calculation considerably, we need to make sure such items don't exist there. So in our example, we know the 30 stories are worth 360 story points. </p><p> We've observed that our ratio of unpredictable to known stuff is 0,4:1. So 360 * 0,4 = 144. That means that even though we now see stuff for 360 points in our list, it is probable that by the time we finish the last one , we will actually make another (of course <i>roughly</i>) 144 points of work that we don't know about yet. So in total, we will have <i>roughly</i> 500 points of work to do. </p><p> Knowing our velocity (let's stick with 50 points per sprint), let's divide 500 / 50 = 10. So we can conclude that to finish the thirtieth item in our list, it will take us <i>roughly</i> 10 sprints. It might be 8 or it might be 12, depending on the deviations in our velocity and the team's maturity. </p><h2>Additional decisions we can take</h2><p> Two common types of questions that we can now answer: </p><ol><li> It's the first of January and we have 2-week long sprints with the team from the previous example. Are we able to deliver all of the 30 items by March? Definitely not. Are we able to deliver them by December? Absolutely. It seems that they will be dealt with sometime around May or June. </li><li> We know our budget will last for (e.g.) 4,5 months from now. Will we be able to deliver those 30 items? If things go optimistically well, it might be the case. But we should evaluate the risk and decide accordingly. </li></ol><p> How can we act upon this? We can now systematically influence the variables in order to increase our chances of fulfilling the plan. A few options out of many: </p><ul><li>We can try to raise the team's velocity by adding a developer if that's deemed a good idea.</li><li>We can try to simplify some stories in the backlog to make the amount of known work smaller.</li><li>Or we can push the plan's end date.</li></ul><p> A warning: Some choose an approach to let everything be constant and try to increase the velocity by “motivating” (understand forcing) the team to plan more story points for a sprint. I don't need to explain that this is a dead-end that, statistically speaking, leads to the most likely scenario of having something “fall over” from the sprint backlog. It burdens the team with the unnecessary overhead of having to deal with the consequences of overcommitment during the sprint and work that won't get done any faster anyway. We can rather review the development tools and processes to see if there is any chance for velocity improvement, but that should be a permanent and continuous activity for any team regardless of plans. </p><h2>Final words</h2><p> Planning projects is never an exact process. But there are certain statistics and metrics that can give us guidelines and help us see how realistic various plans are. We can then distinguish between surefire plans, totally unrealistic plans, or reasonable ones. It can tell us when we should be especially cautious and take action to increase our chances. </p><p> But any predictions will only be as precise as we are transparent and honest with ourselves when getting the statistics. Trying to obscure anything in order to pretend there are no unforeseen factors or problems will only make the process more unpredictable in the long run. </p><p> So hopefully this article will inspire you on how to tackle the future in a more comfortable way. </p>​<br>#scrum;#agile;#project-management;#release-management
So you want to create a design system, pt. 2: Colors you want to create a design system, pt. 2: Colors<p>Color is probably the most distinctive element of any design, and also the most important expression of brand identity (at least until <a href="">Material You</a> completely reverses this relationship, but it remains to be seen how it will be adopted). So how do we approach color when designing and implementing a design system so that our solution is usable, versatile, and scalable?</p><h2>Color me curious</h2><p>Besides conveying the brand and evoking emotions, colors have several other roles in current applications, including:</p><ul><li>highlighting different application states such as errors, warnings, success, or info messages </li><li>ensuring usability, legibility, and accessibility of the application under all conditions </li><li>providing different themes from which the user (or system) can choose according to environmental conditions or personal preferences </li></ul><p>Regarding the last point, users nowadays expect support for at least light and dark themes. Often this is more than just an aesthetic choice - for example, a car navigation app that dazzles drivers at night with large areas of bright colors can be downright dangerous.</p><p>And while the app supports switching between themes, it doesn't have to stop at just these two basic ones, for example:</p><ul><li>Is accessibility extremely important to your app? Add a specially designed high-contrast or colorblind-friendly theme. </li><li>Does the app owner currently run a major promotion, have an anniversary, or celebrate some significant event? Make it known with a special temporary theme. </li><li>Do you want to differentiate products or make it clear that the customer bought a premium version of the app or service? Add a special, more luxurious-looking theme. </li></ul> <img alt="Various application themes" src="/Blog/PublishingImages/Articles/design-system-2-01.png" data-themekey="#" /> <p>Theme support is a feature that is unique in that it makes both users and the marketing department happy. But how to construct it so that both designers and developers can actually work with it and be productive?</p><h2>Layers of indirection</h2><p>Let's start with what is definitely not suitable: Hardcoding the colors in the design tool and therefore in the code.</p> <img alt="Do not hardcode colors" src="/Blog/PublishingImages/Articles/design-system-2-02.png" data-themekey="#" /> <p>There are obvious drawbacks to this method, including the inability to change colors globally in a controlled manner (no, “find & replace” isn’t really a good idea in this case), and the need to copy and edit all designs for each additional theme we want to add (for designers), or cluttering the code with repetitive conditions (for developers). It also often leads to inconsistencies and it’s extremely error-prone - did you notice the mistake in the picture above?</p><p>Unfortunately, we still occasionally encounter this approach because many design tools will happily automagically present all the colors used, even if they are hardcoded, creating the illusion that the colors are under control and well-specified. They aren’t. Don’t do this.</p><p>So how to get rid of hardcoded colors? The first step is to hide them behind named constants and reuse these constants in all places.</p> <img alt="Do not use color constants alone" src="/Blog/PublishingImages/Articles/design-system-2-03.png" data-themekey="#" /> <p>This is definitely better - the colors can be changed globally in one place, but the problem arises when supporting multiple themes. The naive solution is to override each constant with a different value in every theme. This works as long as the colors in the different themes change 1:1. But consider the following situation:</p> <img alt="Do not override color constants per theme" src="/Blog/PublishingImages/Articles/design-system-2-05.png" data-themekey="#" /> <p>Since it is usually not advisable to use large areas of prominent colors in a dark theme, although the toolbar and button in a light theme are the same color, the toolbar should be more subdued in a dark theme. This breaks the idea of overriding the colors 1:1 in different themes because where one theme uses a single color, another theme needs more colors.</p><p>The solution to this situation is the so-called (and only slightly ironic) <a href="">fundamental theorem of software engineering</a>:</p><p style="text-align:center;"><em>“We can solve any problem by introducing an extra level of indirection.”</em></p><p>In this case, that means <em>another</em> layer of named color constants. I kid you not - please stay with me, it’ll be worth it.</p><h2>The solution</h2><p>We achieve our goals, i.e. the ability to easily and safely change colors globally, and support any number of themes, by following these steps:</p><ol><li><strong>Define a set of semantic colors.</strong> These are colors named and applied based on their purpose in the design. Their names must not express specific colors or shades, but <em>roles</em>. For example, Google’s Material Design defines the following semantic colors: <img alt="Semantic colors of Material Design" src="/Blog/PublishingImages/Articles/design-system-2-06.png" data-themekey="#" /> These names are a good starting point, but of course, you can define your own, based on your needs. What's important is that semantic colors don't have concrete values by themselves, they are placeholders or proxies that only resolve to specific colors when applied within a specific theme, meaning one semantic color will probably have a different actual value in each theme. </li><li><strong>Define a set of literal colors.</strong> These constants literally represent the individual colors of your chosen color palette. They are common to all themes, so there are usually more of them than semantic colors. Unlike semantic colors, they are named purely on the basis of their appearance. For example, an earlier version of Material Design defined the following shades: <img alt="Old Material Design literal colors" src="/Blog/PublishingImages/Articles/design-system-2-07.png" data-themekey="#" /> Recently it has become a common practice to distinguish colors with different lightness using a number where 1000 is 0% lightness (i.e. black) and 0 is 100% lightness (white), but of course you can devise your own system. </li><li>Follow this rule in both design <em>and</em> code (no exceptions!):<br><strong>Semantic colors must be used exclusively and everywhere. Literal colors (or even hard-coded constants) must <em>never</em> be used directly.</strong><br> This means that the use of colors in design and implementation must have the possibility of being completely specified in the form of "wireframes" like this: <img alt="Design wireframe specified with semantic colors" src="/Blog/PublishingImages/Articles/design-system-2-08.png" data-themekey="#" /> </li><li><strong>Finally, map semantic colors to concrete literals <em>per theme</em>.</strong> This step ultimately produces a specific theme from the design specification, which is in itself <em>independent</em> of a particular theme. Based on our previous example, the final result will look like this: <img alt="Themes resolved from semantic colors mapped to color literals" src="/Blog/PublishingImages/Articles/design-system-2-09.png" data-themekey="#" /> For example, toolbar background color is <em>specified</em> as <span class="pre-inline">Primary</span>, which in <span class="pre-inline">Light</span> theme is <em>mapped</em> to <span class="pre-inline">Purple700</span> literal color, but in <span class="pre-inline">Dark</span> theme it resolves to <span class="pre-inline">Purple900</span>. The most important thing is that <span class="pre-inline">Purple900</span> or <span class="pre-inline">Purple700</span> literal colors <em>aren't</em> mentioned in the design specification, only in theme definition. </li></ol><p>It's just a little extra work, but the benefits are enormous. We have successfully decoupled the <em>definition</em> of the colors from the <em>actual</em> colors used in various themes. </p><h2>Make it work for you</h2><p>There are usually questions that often arise or choices that need to be made when implementing this system. Here are some tips based on our experience:</p><ul><li><strong>Don't go overboard with the number of semantic colors.</strong> It's tempting to define a separate semantic color for every part of each UI element (e.g., <span class="pre-inline">ButtonBackground</span>, <span class="pre-inline">SwitchTrack</span>, <span class="pre-inline">ProgressIndicatorCircle</span>), which has the theoretical advantage that you can then change them independently, but it also makes it much harder to navigate the design and implementation. The ideal amount of semantic colors is one where one can hold more or less all of them in one's head at once. Try to find a minimum set of sufficiently high-level names that will cover 90% of situations. You can always add more names later. </li><li><strong>Naming is hard.</strong> Since semantic colors form the basis of the vocabulary used across the team and also appear everywhere in the code, it's a good idea to spend some time looking for the most appropriate names. If some of the chosen names turn out to be not that fitting, don't be afraid to refactor them. It's unpleasant, but living with inappropriate names for a long time is worse. </li><li><strong>Never mix literal and semantic names.</strong> For example, a set of semantic colors containing <span class="pre-inline">Orange</span>, <span class="pre-inline">OrangeVariant</span>, <span class="pre-inline">Secondary</span>, <span class="pre-inline">Background</span>, <span class="pre-inline">Disabled</span>, etc. isn’t going to work well, even if the main color of your brand is orange and everyone knows it. Even so, create a purely semantic name for such a color, like <span class="pre-inline">Brand</span> or <span class="pre-inline">Primary</span>. </li><li><strong>If you need multiple versions of a semantic color, never distinguish them with adjectives expressing properties of literal colors</strong> such as <span class="pre-inline">BrandLight</span>, <span class="pre-inline">BrandDark</span>, etc., because what is darker in one theme may be lighter in another and vice versa. Instead, use adjectives expressing purpose or hierarchy, such as <span class="pre-inline">BrandPrimary</span>, <span class="pre-inline">BrandAccent</span>, or even <span class="pre-inline">BrandVariant</span> (but if you have <span class="pre-inline">Variant1</span> thru <span class="pre-inline">Variant8</span>, you have, of course, a problem as well). </li><li><strong>For each semantic color that can serve as a background color, define the corresponding semantic color for the content that can appear on that background.</strong> It's a good idea for these colors to contain the preposition <span class="pre-inline">on</span> or the word <span class="pre-inline">content</span> in the name, like <span class="pre-inline">OnPrimary</span> or <span class="pre-inline">SurfaceContent</span>. Avoid the word <span class="pre-inline">text</span> (e.g., <span class="pre-inline">SurfaceText</span>), as this color will often be applied to other elements such as icons or illustrations, and try to avoid the word <span class="pre-inline">foreground</span> because sometimes the use of background and foreground colors can be visually inverted: <img alt="Two components with inverted colors" src="/Blog/PublishingImages/Articles/design-system-2-10.png" data-themekey="#" /> </li><li><strong>The use of the alpha channel in literal colors is a difficult topic.</strong> Generally speaking, the colors that will be used as backgrounds should be 100% opaque to avoid unexpected combinations when several of them are layered on top of each other (unless this effect is intentional). Content colors, on the other hand, can theoretically contain an alpha channel (useful, for example, for defining global secondary or disabled content colors that work on different backgrounds), but in this case, it is necessary to verify that the given color <em>with its alpha value</em> works with any background.<br>Another question is alpha channel support in your design tool and code - is the alpha value an integral part of the color, or can we combine separate predefined colors and separate predefined alpha values? </li><li><strong>If your design tools don't directly support semantic colors or multiple themes at the same time, work around that.</strong> Tools come and go (or, in rare cases, are upgraded), but your design system and the code that implements it represents much more value and must last longer. Don’t be a slave to a particular tool. </li><li><strong>All text should be legible and meet accessibility standards</strong> (icons on the other hand don’t need to do that, but it’s generally a good idea for them to be compliant as well) - see <a href="">The Web Content Accessibility Guidelines (WCAG 2.0)</a>, and use automated tools that check for accessibility violations. </li></ul><h2>Design is never finished</h2><p> it's important that your design system is able to evolve sustainably. This way of defining color, although certainly not the simplest, allows for exactly that. We'll look at other fundamental elements of design systems and how to handle them next time.</p>#design-system;#ui;#ux;#development;#android;#iOS
Scrum smells, pt. 6: Unknowns and estimates smells, pt. 6: Unknowns and estimates<p>Today, I'd like to share some of the ideas and estimation approaches that helped us in past projects. The tricky part in long and short-term planning is how to predict the unknowns that will influence us in the future. As I wrote earlier, there are several things that usually come up and may not be visible in the product backlog when you are planning something.</p><h2>The unknowns</h2><p>In projects related to mobile app development, we usually encounter the following unplanned activities:</p><ul><li>Defect fixing</li><li>Backlog refinement activities</li><li>Collaboration on UI/UX design</li><li>Refactoring</li><li>New user stories</li></ul><p>Defect fixing is quite obvious and we have spoken about it already. You can't usually foresee what bugs will appear.</p><p>Backlog refinement activities include understanding the backlog items, analyzing the underlying technical and usability aspects, and making the backlog items meet the definition of ready. </p><p>The UI/UX design process is not just a simple decision about colors and shapes. The controls used and the screen layouts and flows usually have a large impact on how the application needs to be built, and we witness over and over again that a seemingly small aspect of the design idea can have a vast impact on the complexity of the actual implementation. So in order to keep the cost/benefit ratio reasonable, we have learned that it is necessary that the developers collaborate closely with the designers in order to prevent any unpleasant surprises. You can read more about this topic in <a href="/Blog/Pages/design-system-1.aspx">this blog series</a>. </p><p>Refactoring existing code and infrastructure setup is a must if we want to develop a product that will be sustainable for longer than a few weeks. It can also have the potential of making the dev team more effective.</p><p>New user stories are interesting. You invest a lot of time into the backlog refinement and it just looks perfect, everything is thought through and sorted. Fast forward two months into the future and you discover (with new knowledge from that past two months) that you need to simplify some stories while others have become obsolete, but more importantly, you realize that you need to introduce completely new features that are vital for app's meaningfulness. You couldn’t see this before you had the actual chance to play around with the features from the past couple of months and gather feedback from users, analyze the usage stats or see the economical results.</p><h2>Estimates</h2><p>Having most of the stuff in the backlog estimated for its complexity (size) is vital for any planning. But as we have all probably learned the hard way, estimates are almost always anything but precise. We, therefore, did not find any value in trying to produce exact estimate values (like 13.5 man-days of work), but we rather use the approach of relative estimation while using pseudo-Fibonacci numbers: 0, 1, 2, 3, 5, 8, 13, 20, 40, 100.</p><p>It is important to understand that these are dimensionless numbers. They are not hours, man-days, or anything similar. It is an abstract number used solely to set a benchmark and compare other items against each other.</p><p>So what does that mean? At the beginning of the project we pick an item in the backlog that seems to be of a common size and appears neither small nor big, a number between the 5-8 range. That will be our benchmark and all other stories are then compared to it. How much more difficult (or easy) is this or that item compared to our benchmark?</p><p>Over time, we usually found out that the initial benchmarks and estimates were completely off. But that is OK, it's a learning process. It is important to review the estimates after the actual development and from them. Was that user story really an 8? Were these two items as similar as we initially thought? If not, how would we estimate them now and why? That also means that from time to time it's necessary to revisit all the already estimated items in the product backlog. </p><p>It usually is not necessary to go into deep details with stuff that is several sprints ahead. As the team gains experience with the product domain, the developer's gut feelings get more relevant and precise. That means useful estimates can be done quite swiftly after the team grasps the particular feature's idea. Sure, some stuff in the backlog will be somewhat underestimated, some overestimated. But with long-term planning and predictions it usually suffices because statistically, the average usually gets quite reliable.</p><p>The outcome of all this is a backlog where every item is labelled with its size. It becomes clear what items are meaningfully defined. The development team has an idea about the technical solution (meaning that the size is reasonable) and what items are completely vague or for which the team members lack key business or technical information. Those are usually the items with estimates labels of “40”, “100”, or even “??”.</p><p>If such inestimable stories are buried in the lower parts of the backlog and the product owner does not even plan to bring them to the market for a long time from now, that's fine. But do any of these items have a high value for the product and do we want to bring it to the market soon? If that's the case, it sends a clear message to the product owner: back to the drawing board, let's completely re-think and simplify such user stories and expect that some team capacity may be needed for technical research. </p><p>So after all this hassle, the upper parts of the backlog will have numbers that you can do math with.</p><h2>Quantifying unexpected work</h2><p>The last piece of the puzzle requiring predictions and plans is to quantify how much of the unexpected stuff usually happens. Now, this might seem like a catch-22 situation - how can we predict the amount of something that we can't predict by its definition? At the beginning of the development, this is indeed impossible to solve. But as always, agile development is empirically oriented - over time we can find ways to get an idea about what is ahead based on past experience. As always, I am not preaching any universal truth. I am just sharing an experience that my colleagues and I have gathered over time and we find useful. So do we do it? </p><p>It's vital to visualize any team's work in the product and sprint backlog as transparently as possible. So it's also good to include all the stuff that are not user stories, but the team knowingly needs to put some effort into them (like the known regressions, researches, refactorings, etc.) into the backlog too. If it's possible to estimate the size upfront, let's do it. If it's not, either cap the maximum capacity to be invested or re-visit and size the item after it's been done. This is necessary in order to gather statistics. </p><p>Just to be clear - let's not mistake such unexpected work with a scope creep. I assume that we don't suffer from excessive scope creep, the unexpected work is indeed solely highly valuable and necessary work that was just not discovered upfront.</p><p>So now we have a reasonably transparent backlog, containing the originally planned stories and also the on-the-go incoming items. We have most of it labelled with sizes. In the next part of this series, we'll try to make some statistics and conclusions on top of all this. </p>#scrum;#agile;#project-management;#release-management
So you want to create a design system, pt. 1: Can designers and developers ever be friends? you want to create a design system, pt. 1: Can designers and developers ever be friends?<p>Design systems are all the rage these days. Done right, they make both UI design and implementation much easier and, more importantly, cheaper, while providing users with a consistent and pleasant UX.</p><p>Let's look at them from a mobile development perspective - how do you create a design system that is usable, efficient, and even removes the pain points of interaction between designers and developers?</p><h2>What exactly is a design system?</h2><p>A design system is a comprehensive framework for the design and implementation of digital products. It consists of reusable styles, components, standards, and guidelines that are used together to build any number of applications that are both coherent and allow for evolution.</p><p>A design system consists of two parts, described by this highly scientific equation:</p><p style="text-align:center;"><em>Design system = Design × System</em></p><p>The <em>design</em> part is all about how your product looks, behaves, and speaks to the user - in other words, the actual look'n'feel, perhaps developed in more detail and with more depth than usual. This is probably the easier part because historically our industry had quite a lot of experience with this kind of thing.</p><p>The <em>system</em> part is not to be underestimated because it is important for consistency, quick and cheap design and implementation, and the ability to freely shape the UX in the future. Achieving that level of flexibility is difficult, but it is also where design systems bring the most value.</p><p>Since the system part must be robust enough to allow for different designs to be created and then evolved in time while supporting the implementation, we arrive at the second fundamental equation:</p><p style="text-align:center;"><em>Design < System</em></p><p>That's not to say that design is less important or less valuable, of course not! The point is that a situation where you have a great design but a bad system is much worse than the other way around, especially in terms of implementation effort and overall flexibility. </p><p>Designers are better at, well, designing, while developers are probably a little bit better at creating systems. Let's look at how both sides can help each other and make their jobs easier.</p><p>I was lucky enough to recently implement a large-scale design system used in dozens of applications. Based on this experience, before we get to the nitty-gritty details of design systems specs and implementations, let me offer some initial tips for both designers and developers.</p><h2>Dear designers</h2><ul><li>You absolutely need to have first-hand experience with <em>all</em> the platforms you will be designing for. You need to immerse yourself in the platform, soak up the feeling of using it on a daily basis, and get to know its iconic apps.<br> If your main phone is an iPhone, you need to get an Android device too (and vice versa) - yes, I know, the culture shock is considerable in both cases, but this is your job after all. It doesn’t matter if you're going to design for cars or smart fridges or fighter jets or IoT-enabled dog houses, you need to physically spend some time with those things. </li><li>Be aware that different platforms have wildly different UI idioms, widget sets, features, and development costs, not forgetting different and sometimes very interesting bugs! UI programming is actually <em>very</em> hard (that’s an actual fact, not me complaining, I swear!) and what is free on one platform can be very expensive or almost impossible to do on another.<br>Even worse, there is usually no obvious, predictable relationship between what is simple and what is complex to implement. Even a small change in design from your perspective can cause a massive increase in the implementation effort. Be prepared to kill your darlings or at least postpone them until the next major version. </li></ul> <img alt="Two different buttons" src="/Blog/PublishingImages/Articles/design-system-1-01.png" data-themekey="#" /> <p style="text-align:center;"><em>On Android, one of these buttons is actually much more expensive to implement than the other.</em> </p><ul><li>If you don’t design it, someone else will - and that someone is likely to be someone <em>different</em> each time, so not only will some things in your app be out of your control, but they'll also look a little different every time.<br> There is no other option but to simply design, in sufficient detail, <em>everything</em> your design system will need. </li><li>Sooner or later your design will be transformed into code, in the best case 1:1. There is a lot of UI code in most applications and it is complex, expensive, and also very hard to test. How consistent and systematic your specs and designs are <em>directly and significantly</em> affects the cost of development. Consistency doesn't just help developers, it also helps you as well when designing real applications. </li><li>It is often prohibitively expensive to achieve a pixel-perfect implementation. I know it's hard, but sometimes achieving a <em>good enough</em> result is the sensible thing to do. Because the exact reasons for this are usually highly complicated, technical, and thus boring and uninteresting, you might need to trust your developers more or less blindly in this regard. </li></ul> <img alt="Text rendered twice" src="/Blog/PublishingImages/Articles/design-system-1-02.png" data-themekey="#" /><br> <p style="text-align:center;"><em>Same font size, same letter spacing - but the design tool and OS render it slightly differently anyway. And let's not start with inconsistencies across OS versions...</em></p><ul><li>Unless you are designing a special kind of application such as games or VR, please respect the platform and therefore its users. The goal is to strike the right balance between the platform itself, your customer's brand, and your creativity.<br>For example, people don't compare the iOS and Android versions of your app to each other - what they do is compare your app to other apps on their device. </li><li>Modern design tools are great, but they still have a lot of limitations that may not be obvious from your point of view - things like layout composition, exporting assets to different formats, or code generation look good at first glance, but in reality are often not completely usable. Please don't fall blindly for the marketing of the manufacturers of these tools and discuss this with your developers. </li></ul> <img alt="Bad UI source code" src="/Blog/PublishingImages/Articles/design-system-1-03.png" data-themekey="#" /><br> <p style="text-align:center;"><em>I won't name a specific tool here, but someone can commit this garbage to source control only over my dead cold body.</em></p><ul><li>Since you are the guardians of the source of truth of your design system, you must have a mechanism in place to communicate changes and updates to your design system. </li><li>Please talk to your developers often and be prepared to meet them halfway. Create great things and don’t forget to have fun in the process! </li></ul><h2>Dear developers</h2><ul><li>Because no one sees or understands the technical details as you do, it is your responsibility to communicate potential problems and difficulties in a simple, clear, and timely manner. Not only that, you need to be able to offer alternative solutions or trade-offs based on different criteria. </li><li>In a sense, you're also responsible for the delivery of the whole thing, so you have to be able to schedule your work on the whole application from a technical perspective so you don't end up with two perfect screens and 30 broken ones. </li><li>If you think something is missing from the design system, please let the designers know! Don't jump straight into implementation - let the designers do their job. Let them design the thing so it's consistent with the rest of the design system, and let them specify it so it's reusable. </li><li>You need to have a perfect overview of the UI of your platform, both from a development and user perspective. These APIs are huge in width and depth and contain many bugs and incompatibilities, but it's your responsibility to know most of them. </li></ul> <img alt="Android View class reference" src="/Blog/PublishingImages/Articles/design-system-1-04.png" data-themekey="#" /><br> <p style="text-align:center;"><em>54 subclasses is overly optimistic. And the whole thing is over 30.000 LOC long!</em></p><ul><li>As I said, there is a lot of UI code in a normal application and it is complex, expensive, and very hard to test, but also highly reusable. For these very reasons, you must <em>not</em> treat it as a second-class code! The usual principles of clean code apply here too - buzzwords like DRY, KISS, YAGNI, or separation of concerns are actually pretty good ideas. Hardcoding and duplicating things is a road to maintenance hell. </li><li>Since your implementation will ideally follow the designers' specifications and designs very closely in the code, the quality of input from the designers has a high chance of directly affecting the quality of your code.<br>If necessary, politely but firmly demand cleanliness and consistency in design system specifications, which, after all, must always be the only source of truth in the design system.<br>And since the implementation follows the design system specifications, it is also your responsibility to keep the implementation in sync with the design system over time. </li><li>It helps if you have a basic understanding of design principles, typography, color theory, etc. You don't have to do landscape painting in your spare time, but being able to talk to designers at least partially in the language of their domain is important. A light form of OCD that allows you to quickly spot misaligned elements or inconsistent colors comes in handy too. </li></ul> <img alt="OCD test" src="/Blog/PublishingImages/Articles/design-system-1-05.png" data-themekey="#" /><br> <div style="text-align:center;"><em>One of these things is not like the other.<br><br></em></div><ul><li>Please talk to your designers often and be prepared to meet them halfway. Create great things and don’t forget to have fun in the process! </li></ul><h2>A successful cooperation</h2><p>The above points help designers and developers to be on the same page when working together, which is key for a successful outcome. Next time, we'll get our hands dirty with specific elements of design systems such as colours, typography, layouts, and motion, bringing the <em>designs</em> and <em>systems</em> together in a scalable and maintainable way.</p>#design-system;#ui;#ux;#development;#android;#iOS
Product development: Perfection vs. minimalism development: Perfection vs. minimalism<p>Today I'd like to address one of the common phenomena which lead to cost and timeline inflation of projects. Maybe you have witnessed something similar: At the beginning of a project, the team makes a (rough) analysis of what will need to be done, how complex those individual parts are and how long (roughly) the development will take.</p><p>Say we're creating a simple email client application. The key parts we know of are the message inbox, message viewing, email writing, sending, and logging into the email account. Just a few key areas, nothing complicated. At the beginning of this project, the team reckons that spending a month on each of these areas should more than suffice.</p><p>They set off developing. After the login functionality, the product owner prioritises the development of the message inbox. The devs put the inbox together and after 3 weeks they claim it is done.</p><p>The testers and the product owner inspect it. They fill in the username and password, push the button and see a list of e-mail messages - it works! But the product owner and the key users think there is just too little information visible in the inbox's message cells and want to improve it. So what if we added two lines of text preview instead of one? Or is one better, so the user can view more messages at once? Or maybe let the user decide for himself and let's add a setting so that the user can set how many lines of message preview he wants to see.</p><p>It's also not visible which people were CC’d. So let's introduce another setting, where the user can toggle the display of CC'd people in the inbox. There is also no indication if an email has an attachment or not. So of course, while we're at it, let's put an icon there to indicate attachments. Also, some mail clients group message threads together in the inbox - that's a cool thing we'd definitely like to have in our app too! And just like that, there comes message grouping.</p><p>Let's do all of this now so that we have the inbox completely done and we don't need to get back to it later.</p><h2>The perfection pitfall</h2><p>I think you see where I'm heading to. Weeks pass by and we're still doing the inbox. After 3 months, we have a pimped-up inbox that everyone is proud of. We've taken half the time originally estimated for the whole project just for one feature area. But what value is the whole application to the actual users? Still very little. I can just view my messages. In a very fancy and configurable way, true. But I still can't open the message and reply back. There will be some sleepless nights later in the project.</p><p>Everyone wants to have things <em>perfect</em> - meaning nothing else can be added. It's a natural tendency of people and I've seen that on projects over and over again. Project managers, product owners, developers, key users, stakeholders, all of them want to have that perfect thing.</p><p>It seems logical to develop features that contextually belong together at once and make that application part perfect, like doing all inbox-related stuff at once. In my opinion, this is one of the biggest project traps - it leads to scope creep and the actual project goal drifts off.</p><h2>Minimum viable product</h2><p>Is there a better approach? I believe there is, but it requires discipline. Firstly, we need to cut the application by user stories (end-to-end use cases), which bring actual value to users. Is a stand-alone log-in form worth something to the user, if the app then does nothing else? No. Is a Reply button worth something, if you can't send the message afterward? No.</p><p>We need to build something that is useful and usable, not a bunch of independent technical parts. Only then can we also verify the product's function. We are building apps for the end-users, so we need to assess the application's quality from their point of view. In other words, we need to cut the items into the product backlog vertically - across all technical layers, not horizontally. In such a way, it incorporates all the necessary technical aspects to finish a use case.</p><p>Secondly, we need to know why we're building the product. Maybe you've heard of the <em>minimum viable product</em>> approach. An MVP is a product with the least functionality possible, but fully usable, useful, and enabling gathering feedback from users as soon as possible. Put another way, you can't remove anything more from the product while still keeping the product useful. We're doing the bare minimum to solve the user's main and most basic need.</p><h2>Key questions to ask</h2><p>The team (not just the product owner) must understand from the very beginning the answers to the following two questions:</p><ol><li>What is the problem we're trying to solve? What is the most basic nature of the problem we're sorting out for the user? </li><li>Who are the people using the application?</li></ol><p>With this in mind, every single backlog item can then be critically evaluated:</p><ol><li>Is this item/function solving the core user's problem?</li><li>How can we simplify it and still solve the core problem?</li><li>Isn't there any backlog item more important, without which the app does not solve the core problem and is therefore meaningless? </li></ol><p>This approach is basically about figuring out what else is there that you <em>do not need to do now</em> and can leave for later. And doing that in a brutally honest way.</p><h2>Mailing example</h2><p>So what could this look like with our e-mail client application? Let's say the target user is a person who would use the app for his personal email account, not a power-user or a corporate user. The core need of such a user would be basically to be able to view incoming emails, read them, and write new ones.</p><p>Our aim then should be to develop a trivial application doing just the necessary things to finish these use cases and to do it as quickly as possible. Does the user need rich text formatting to send a mail? Definitely not. Does he need marking messages as read? It's a useful feature, but he still can perform vital use cases without it. Does he need to delete a message? Sure it's good to keep the inbox organised, but still - even though not comfortably, he can live without it.</p><p>I am not saying you won't ever add features that are not vital. But the initial aim should be to develop a <em>bare minimum product</em> that does key functions, see how much time we needed, how much did it cost, how many resources do we have left, and then decide on what to do next. With this approach, we increase the chance that before our budget or money runs out, we have at least something useful that we potentially can put to market. This is how we can approach the project risks by managing scope very actively.</p><p>This is contrary to the common situation, where we have certain areas of the product working, including nice-to-have features, but we're still lacking coverage of other key use cases even in a very minimal way. That's bad, because now the product can't be used at all, and resources spent are sunk unless we invest more to finish the critical stuff. It is worthless at this stage.</p><p>Imagine you need to present both variants to the project sponsor after running out of resources. Will it be easier to say that the application is essentially working, but there are not enough resources to make it more user-friendly? Or that you have a cool inbox, but the user still can't send an email?</p><p>So once we have the MVP, a good idea is to give it to a group of users to gather feedback from them as soon as possible. If we still have some project resources left, let's use them to make the product better, like developing marking messages as read.</p><h2>On the right way to good quality product</h2><p>I hope you did not get the impression that I am suggesting building a product with a poor user experience. That is not true. The basic idea I am presenting is to get the most out of the resources we have. Let's act as if everything could go wrong and our resources were extremely scarce.</p><p>And if we build our MVP and see we still have enough time and money, of course, let's add more and more cool features. But you'll be adding these features in peace knowing that you have no debt; that if you stopped developing at any moment from now, you made the product most relevant for the users as you potentially could, with the resources you had; that everything in the backlog is less important than what the product already contains.</p><p>Does this guarantee you won't ever miss a project deadline or budget? Not at all. But it gives you a bigger chance it won't happen. You achieved the best possible return on investment given the circumstances.</p>#project-management;#agile;#development
Building a chatbot, pt. 2: Conversations a chatbot, pt. 2: Conversations<p>​​​​Everybody has met or talked to a person that had a script to follow. Insurance salesmen, dealers, receptionists… All these people were given a script to follow, a checklist to go through, or just a simple answer to give when asked. Not unlike a chatbot.</p><p>In the previous article, I described a chatbot that we built for making reservations. Not much was said about the inner works or about its potential. There are indeed multiple topics to cover. In this article, I would like to talk a bit about conversations and conversation flow.</p><h2>AI?</h2><p>Our application is designed to fulfill users’ requests. A user usually has a general idea about what he or she wants to achieve. But even the most knowledgeable user can’t possibly know every item and variations that the application needs to achieve a goal. Even if the user is not sure what the right way to ask is, the chatbot should be able to guide the user and in the end, find exactly what the user desires, and then fulfill his request. For each request, the bot should have a list of instructions to follow - information that the user has to provide so that the application is able to execute the user's command.</p><h2>Illusion of choice</h2><p>To provide a good user experience, we decided to leave the first step to the user. The user’s first sentence gives us a direction to follow and an idea of the user’s intent, but actually, this is the last time a user is in command of the conversation flow. From this point on, every step of the conversation is directed by the application. The bot knows what checklist it has to follow to fulfill the request. Even if the user is not specific or not sure what to say, the bot should be able to get this information from the user with properly constructed questions.</p><p>Every conversation in our application is directed by a set of checklists. These checklists can be simple, even just one item, or they can spawn other checklists, and even create cyclic conversations. What exactly we are able to create with these types of checklists will be described in a separate blog. For now, let’s focus on the following example.</p><h2>From a conversation to a checklist</h2><p>Let’s meet our example user: Joe. Joe is working an office job and has multiple meetings per day. For those, he needs to schedule reservations in a call booth or a meeting room with his colleagues. At the start of the week, he knows about two meetings that will take place. One today (for the sake of this example let’s assume it is Monday) and one tomorrow. Interaction with the bot will then look like this:</p><p style="margin-left:40px;margin-top:20px;margin-bottom:30px;"> <strong style="color:#381457;">User</strong> : <em>Book me a room for today from 11:30 to 12:30</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Alright! Do you want to book a specific meeting room or should I just book you one that is free?</em><br><strong style="color:#ffed00;">Options</strong> : <em>Any meeting room, Specific meeting room</em><br> </p><p>For today's meeting Joe has a remote call with colleagues from out of town, therefore any available room will do the trick. He will select “any meeting room” from the options given. If there is an available room in the system, Joe will get one. Like this:</p><p style="margin-left:40px;margin-top:20px;margin-bottom:30px;"> <strong style="color:#381457;">User</strong> : <em>Book me a room for today from 11:30 to 12:30</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Alright! Do you want to book a specific meeting room or should I just book you one that is free?</em><br><strong style="color:#381457;">User</strong> : <em>Any meeting room</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>I have booked room R.332 for today from 11:30 to 12:30 for you</em><br> </p><p>And that's it. Joe has booked a room for a given time. From a user's point of view this is quite easy. Using our mobile applications, Joe can even use voice input!. This is all great, but how does it work?</p><p>To make a reservation, the chatbot needs to pass a checklist. For the example above, where the user is making a room reservation, the checklist looks like this:</p><ul><li>Intent - make a booking</li><li>What (seat, room, call booth)</li><li>Where (any available room, or does the user want a specific room?)</li><li>When (date and time, or duration of the meeting)</li></ul><p>Joe knew most of these and intuitively created a sentence that matched his wishes best. What was not explicit in the first sentence, the bot asked for in the follow-up question. When asking follow-up questions, chatbot usually gives users multiple options to select from. These suggestions give the user an idea of how to respond and also what kind of information the bot is expecting. In the same answer, the user can also write additional information.</p><p>Now let's say that Joe needs a specific room for a meeting that takes place on Wednesday. He needs a big interactive board, a projector and a room for 10 people where he will introduce new products to customers. This room is quite busy so he needs to find a spot for his meeting. The conversation could look like this one:</p><p style="margin-left:40px;margin-top:20px;margin-bottom:30px;"> <strong style="color:#381457;">User</strong> : <em>When is room R.121 available?</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>For which date(s) do you want to see the room's availability?</em><br><strong style="color:#381457;">User</strong> : <em>Wednesday</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>How long will you need the room for?</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>I will find you times, where there is enough space for your meeting. For example: 1 hour 30 minutes or just like 15 minutes</em><br><strong style="color:#381457;">User</strong> : <em>2 hours</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Alright.</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>The room R.121 is available on wednesday from 9:00 to 13:59.</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Do you want to book it?</em><br><strong style="color:#381457;">User</strong> : <em>Yes</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Ok, can you specify the time for me please?</em><br><strong style="color:#381457;">User</strong> : <em>10:00</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>I have booked room R.121 on wednesday from 10:00 to 12:00 for you.</em><br> </p><p>That's a slightly different conversation, right? But in the end, Joe will have his reservation ready. So what does the checklist on the bot’s side look like for this example?</p><ul><li>Intent - check availability</li><li>What - specific place to check</li><li>When - time-frame to check</li><li>How long - meeting duration</li><li>To book it or not</li></ul><p>Up to this point Joe has only checked for available time slots. If he wants to make a booking now, he can. For booking, the chatbot has a different checklist. That one was previously listed, but here it is again for completion:</p><ul><li>Intent - make a booking</li><li>What (seat, room, call booth)</li><li>Where (any available room, or does the user want a specific room?)</li><li>When (date and time, or duration of the meeting)</li></ul><p>Most of these parameters are already checked for the booking. Joe has already told the bot to look for a specific room. He’s even given it a date so the only thing missing is the time. Joe gave the bot a meeting duration when he was checking for room availability. Now if Joe selects only the start of the meeting, the application already knows the duration and so it will make the booking last 2 hours.</p><p>But wait! That’s not all. Up to this point, all user’s requests had no problems. Let's say that our Joe has had a long vacation, he is back in the office again, but lost track of dates:</p><p style="margin-left:40px;margin-top:20px;margin-bottom:30px;"> ​ <strong style="color:#381457;">User</strong> : <em>Book</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Do you want me to book you a seat or a meeting room?</em><br><strong style="color:#381457;">User</strong> : <em>Meeting room</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Alright do you want to book a specific meeting room or should I just book you one that is free?</em><br><strong style="color:#381457;">User</strong> : <em>What is available?</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>For which time frame(s) do you want to see available rooms?</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>You can tell me any time you want or you can use one of shortcuts bellow.</em><br><strong style="color:#381457;">User</strong> : <em>20.3.2008</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Unfortunately you cannot book or check seat availability in the past.</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Please tell me a time or a time range you want to see available rooms for.</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>For example: Today, Tomorrow, Thursday, 24.10. or something like next week or from 10:30 to 11:15</em><br><strong style="color:#381457;">User</strong> : <em>Ok what about tomorrow?</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Here is a list of available meeting rooms in your preferred area for tomorrow:</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>1. R.121</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>1. R.122</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>1. R.123</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>1. R.124</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>1. R.125</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>These are the first ones I found. But there are some more.</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Do you want to have some of these? You can also tell me, which specific one do you want to have</em><br><strong style="color:#381457;">User</strong> : <em>Yes</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Alright</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>What room would that be?</em><br><strong style="color:#381457;">User</strong> : <em>R.124</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>Can you specify the time for me please?</em><br><strong style="color:#381457;">User</strong> : <em>From 10:00 just for a quick call</em><br><strong style="color:#9d9d9d;">Bot</strong> : <em>I have book room R.124 for tomorrow from 10:00 to 10:30 for you.</em><br> </p><p>Even in case of a longer conversation and with an invalid date, the bot was able to provide Joe with a reservation.</p><p>Again, I will show you the checklist that the chatbot had to go through:</p><ul><li>Intent - make a booking</li><li>What - meeting room</li><li>Specification:<br> <ul><li>Check available meeting rooms</li><li>When - with validation</li><li>Want to book one?</li></ul></li><li>Specific room selection</li><li>When - with specific time selection</li></ul><h2>Think in checklists</h2><p>The main point of these examples is to introduce the checklist philosophy. Every conversation that we have in the system is designed as a checklist with multiple options. It's not a simple checklist where you just confirm an item and move on to the next one. Some of the items can be optional, however at least one has to be mandatory. Each item on the checklist can have its own checklists or policies. There can be policies that end the conversation on the spot or point to another checklist where the conversation continues. Some of the policies and transitions can be seen in the previous examples.</p><p>For example, for time specification we want only the current day or a day in the future, but not in the very distant future. For room bookings, only single-day reservations are allowed. Furthermore, when specifying a room, we have to select from a place that exists in the system database.</p><h2>Conclusion</h2><p>Conversations with our chatbot are based on checklists with predefined policies to follow. The user is given an illusion of choice with a first request that can be created any way the user desires, and afterward the control of conversation flow is entirely coded in the chatbot. From a developer’s point of view, that gives us an easy way to test these conversations, and develop each branch of conversation independently.</p><p>From the user's point of view, the bot asks for missing pieces of information. Thanks to the firm grip on the conversation, the user should not end up in a situation where he doesn't know what to answer or how to reach a certain goal.</p><p>So is it artificial intelligence? No. It's a state machine that follows pre-set lines of conversation. However, the conversation can be very complex, and when it is complex enough, users might come to the conclusion that this is what AI looks like.</p> ​<br>#chatbot;#ai;#neural-network