- We must improve the way we lead and manage IT and reimagine the way everyone across the enterprise views and engages with technology
- A high-performing management framework in practice
- You have to understand why, not just copy the behaviors
- We'll share with you the sights and sounds and experiences of a day at ING, showing you how practices, rhythms, and routines connect to creat a learning organization and deliver high performance and value
- Entering the main space there is a large, open social area designed to create intimate spaces to gather, visit, and share ideas
- After that is the Tribe's suite, where upon entering you'll see a large room with glass walls, creating visibility to the space within
- This is the Obeya room, and is where the Tribe lead's work, priorities, and action items are visualized for the teams and anyone else
- Management meets on a regular cadence here
- Four distinct zones are visualized:
- Strategic improvement
- Performance monitoring
- Portfolio roadmap
- Leadership actions
- Each has current information about targets, gaps, progress, and problems
- Color coding is used to make problems immediately visible
- Each IT objective ties directly, in measurable ways, to enterprise strategy
- Each line of business is organized as a tribe delivering a portfolio of related products and services
- Each tribe is comprised of multiple self-steering teams, called squads, each responsible for a distinct customer mission
- Each squad is guided by a product owner, led by an IT-area lead, and sized according to Bezos' Two Pizza Rule
- Most squads are cross-functional, consisting of engineers and marketers, collaborating as a single team with a shared understanding of customer value
- This team composition is referred to as BizDevOps
- New roles emerge as needed through experience and learning
- There are also chapters, comprised of members of the same discipline, who are matrixed across squads and bring specialized knowledge to promote learning and advancement among squad members
- There are centers of expertise, bringing together individuals with particular capabilities
- There are internal continuous improvement coaches
- The squad workspace is an open area with windows and walls that are covered in visuals that enable the squad to monitor performance in real time, see obstacles, status of improvements, and other information of value to the squad
- Squad visuals share some characteristics; the similarities in Obeya design enable colleagues outside the squad to immediately understand, at a glance, certain aspects of the work, promoting shared learning
- Standard guidelines include
- Visualizing goals
- Present performance and gaps
- New and escalated problems
- Demand
- WIP
- Done work
- Visualizing demand helps prioritize and keep the WIP load small
- Squads have daily stand-up meetings
- Follow a leaders-as-coaches model where everyone's job is to
- Do the work
- Improve the work
- Develop the people
- Develop the people is especially important in a technology domain, where automation is disrupting many technology jobs
- For people to bring their best to the work that may, in fact, eliminate their current job, they need complete faith that their leaders value them
- A fixed percentage of each squad's and chapter's time is allocated for improvement
- Squads think of improvement activities as just regular work
- Jannes' tribes had been challenged by senior leadership to be twice as effective. "There was a tough deadline and lots of pressure. Our tribe lead, Jannes, went to the squads and said, 'If the quality isn't there, don't release. I'll cover your back.' So, we felt we owned quality. That helped us to do the right things."
- Too often, quality is overshadowed by the pressure for speed. A courageous and supportive leader is crucial to help teams "slow down to speed up," providing them with the permission and safety to put quality first
- The coaching team is experimenting with, and measuring, ways to maintain the same high level of collaboration and learning among cross-border squads
- As a leader, you have to look at your own behaviors before you ask others to change
- When you change the way you work, you change the routines, you create a different culture
- Transforming your leadership, management, and team practices
- We are often asked by enterprise leaders:
- How do we change our culture?
- We believe better questions to ask are:
- How do we learn to learn?
- How do I learn?
- How can I make it safe for others to learn?
- How can I learn from and with them?
- How do we, together, establish new behaviors and new ways of thinking that build new habits, that cultivate our new culture?
- Where do we start?
- There is a willingness to experiment with new ways of thinking and working
- You can't "implement" culture change
- Implementation thinking (attempting to mimic another company's specific behavior and practices) is, by its very nature, counter to the essence of generative culture
- A high-performance culture is the development, through experimentation and learning guided by evidence, of a new way of working together that is situationally and culturally appropriate to each organization
- Develop and maintain the right mindset. This is about learning and how to create an environment for shared organizational learning-not about just doing the practices, and certainly not about employing tools. Make it your own. This means three things:
- Don't look to copy other enterprises on their methods and practices, or to implement an expert designed model. Study and learn from them, but then experiment and adapt to what works for you and your culture
- You, too, need to change your way of work. Lead by example. A generative culture starts with demonstrating new behaviors, not delegating them
- Change takes discipline and courage. Practice patience It's going to take time to change actions and thought patterns until they become new habits and, eventually, your new culture
Technology is always changing. It makes the industry interesting and exciting to work in, but it also makes it hard for you, as a developer, to keep up with the changes, let alone get ahead. And yet staying on top of these changes, and thriving because of them, is a rewarding and worthwhile goal, because by doing so, you unlock the potential of what you can accomplish. Here, I explore the how of doing just that.
Friday, November 22, 2019
Accelerate Chapter 16 Discussion Points
Chapter 16 of Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations, shares the experiences of Steve Bell and Karen Whitley Bell applying DevOps and Lean practices at ING Netherlands:
Saturday, November 16, 2019
Kotlin in Action Review and the Book Club Experience
If you are wanting to learn Kotlin, Kotlin in Action will give you a very solid foundation. While the book only covers Kotlin version 1.0 an as such is lacking many of the newer features of the language, the authors, Dmitry Jemerov and Svetlana Isakova, have such a breadth of understanding for the language that you'd be remiss not to read it. (This of course comes as no surprise, considering that they helped design and implement the language.)
I went through this book as part of a developer book club where I took a number of developers through the book along with me. As I went through this book I built out the following exercises to go along with some of the chapters to give the developers a chance to play around with the concepts that they were reading about. When we met to discuss the chapter, we would go through the exercises in a mob programming style, with me casting my laptop screen to a TV, and having the others in the group tell me what code to write to solve the problem, with me giving thoughts and suggestions here and there.
The developers in the book club were mostly all familiar with Java, so for chapters 3 through 6, the exercises that I built dealt with translating code from Java to Kotlin. These exercises went decently well:
For chapter 7 through 11, I changed strategies, and simply had a problem definition to solve without any code to translate. These went much better:
In addition to this, after finishing the book, I was asked present many of the concepts from the book in a series of trainings to a larger group of developers that were unfamiliar with the Kotlin language, along with some help from the other developers that were part of the book club. In addition to doing the training, I created the following outlines of the trainings so that attendees could have something to reference following the training:
All in all, I would say that this book club experience was very successful, with the participants getting a lot out of it and enjoying the process. I will look to reuse and build upon this experience as I help groups tackle technical books and topics moving forward.
Wednesday, November 13, 2019
Accelerate Chapter 11 Discussion Points
Chapter 11 of Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations, covers leaders and managers:
- Transformational leadership
- Leadership has a powerful impact on results
- Being a leader isn't about having people report to you on an organizational chart
- It's about inspiring and motivating those around you
- A good leader affects a team's ability to deliver code, architect good systems, and apply Lean principles to how the team manages its work and develops products
- Transformational leadership is essential for:
- Establishing and supporting generative and high-trust cultural norms
- Creating technologies that enable developer productivity, reducing code deployment lead times and supporting more reliable infrastructures
- Supporting team experimentation and innovation, and creating and implementing better products faster
- Working across organizational silos to achieve strategic alignment
- Engaged leadership is essential for successful DevOps transformations
- Leaders have the authority and budget to
- Make the large-scale changes that are often needed
- Provide air cover when a transformation is underway
- Change the incentives of entire groups of technical professionals
- Leaders are those who set the tone of the organization and reinforce the desired cultural norms
- The five characteristics of a transformational leader are:
- Vision: Has a clear understanding of where the organization is going and where it should be in 5 years
- Inspirational communication: Communicates in a way that inspires and motivates, even in an uncertain or changing environment
- Intellectual stimulation: Challenges followers to think about problems in new ways
- Supportive leadership: Demonstrates care and consideration of followers' personal needs and feelings
- Personal recognition: Praises and acknowledges achievement of goals and improvements in work quality; personally compliments others when they do outstanding work
- What is transformational leadership?
- Transformational leadership means leaders inspiring and motivating followers to achieve higher performance by appealing to their values and sense of purpose, facilitating wide-scale organizational change
- Teams that report leadership in the bottom one-third of leadership strength are only half as likely to be high performers
- Transformational leadership is highly correlated with employee Net Promoter Score
- Leadership helps build great teams, great technology, and great organizations - but indirectly
- Transformational leaders act as an amplifier to the effectiveness of the technical and organization practices discussed thus far
- The role of managers
- We see that leaders play a critical role in any technology transformation
- When those leaders are managers, they may have an even bigger role in affecting change
- Managers play a critical role in connecting the strategic objectives of the business to the work their teams do
- Managers can do a lot to improve their team's performance by
- Creating a work environment where employees feel safe
- Investing in developing the capabilities of their people
- Removing obstacles to work
- Taking measures to make deployments less painful
- Making performance metrics visible and taking pains to align these with organizational goals
- Delegating more authority to their employees
- Knowledge is power, and you should give power to those who have the knowledge
- How can technology leaders invest in their teams?
- Ensure that existing resources are made available and accessible to everyone in the organization
- Create space and opportunities for learning and improving
- Establish a dedicated training budget and make sure people know about it. Also, give your staff the latitude to choose training that interests them. This training budget may include dedicated time during the day to make use of resources that already exist in the organization
- Encourage staff to attend technical conferences at least once a year and summarize what they learned for the entire team
- Set up internal hack days, where cross-functional teams can get together to work on a project
- Encourage teams to organize internal "yak days," where teams get together to work on technical debt. These are great events because technical debt is so rarely prioritized
- Hold regular internal DevOps mini-conferences. We've seen organizations achieve success using the classic DevOpsDays format, which combines pre-prepared talks with "open spaces" where participants self-organize to propose and facilitate their own sessions
- Give staff dedicated time, such as 20% time or several days after a release, to experiment with new tools and technologies
- Allocate budget and infrastructure for special projects
- Tips to improve culture and support your teams
- The most valuable work they can do is growing and supporting a strong organizational culture among those they serve: their teams
- Three things are highly correlated with software delivery performance and contribute to a strong team culture:
- Cross-functional collaboration
- A climate for learning
- Tools
- Enable cross-functional collaboration by:
- Building trust with your counterparts on other teams
- Encouraging practitioners to move between departments
- Actively seeking, encouraging, and rewarding work that facilitates collaboration
- Help create a climate of learning by:
- Creating a training budget and advocating for it internally
- Making it safe to fail
- Creating opportunities and spaces to share information
- Make effective use of tools:
- Make sure your team can choose their tools
- Make monitoring a priority
Tuesday, November 12, 2019
Accelerate Chapter 10 Discussion Points
Chapter 10 of Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations, goes over employee satisfaction, identity, and engagement:
- People are at the heart of every technology transformation
- Employee loyalty
- To understand employee engagement in the context of DevOps, we look at it through the lens of a broadly used benchmark of customer loyalty: Net Promoter Score (NPS)
- High performers have better employee loyalty, as measured by employee Net Promoter Score (eNPS)
- Measuring NPS
- Net Promoter Score is calculated based on a single question:
- How likely is it that you would recommend our company/product/service to a friend or colleague?
- Net Promoter Score is scored on a 0-10 scale, and is categorized as follows:
- Customer who give a score of 9 or 10 are considered promoters
- Promoters create greater value for the company because they tend to buy more, cost less to acquire and retain, stay longer, and generate positive word of mouth
- Those giving a score of 7 or 8 are passives
- Passives are satisfied, but much less enthusiastic customers. They are less likely to provide referrals and more likely to defect if something better comes along
- Those giving a score from 0 to 6 are detractors
- Detractors are more expensive to acquire and retain, they defect faster, and can hurt the business through negative word of mouth
- In this study, two questions were asked to capture employee Net Promoter Score:
- Would you recommend your ORGANIZATION as a place to work to a friend or colleague?
- Would you recommend your TEAM as a place to work to a friend or colleague?
- Companies with highly engaged workers grew revenues two and a half times as much as those with low engagement levels
- Employee Net Promoter Score was highly correlated with:
- The extent to which the organization collects customer feedback and uses it to inform the design of products and features
- The ability of teams to visualize and understand the flow of products or features through development all the way to the customer
- The extent to which employees identify with their organization's values and goals, and the effort they are willing to put in to make the organization successful
- NPS is calculated by subtracting the percentage of detractors from the percentage of promoters. For example, if 40% of employees are detractors and only 20% are promoters, the Net Promoter Score is -20%
- Changing organizational culture and identity
- People are an organization's greatest asset, yet so often they're treated like expendable resources
- When leaders invest in their people and enable them to do their best work, employees identify more strongly with the organization and are willing to go the extra mile to help it be successful
- In return, organizations get higher levels of performance and productivity
- To measure identity, people were measured on the following statements:
- I am glad I chose to work for this organization rather than another company
- I talk of this organization to my friends as a great company to work for
- I am willing to put in a great deal of effort beyond what is normally expected to help my organization be successful
- I find that my values and my organization's values are very similar
- In general, the people employed by my organization are working toward the same goal
- I feel that my organization cares about me
- In today's fast-moving and competitive world, the best thing you can do for your products, your company, and your people is institute a culture of experimentation and learning, and invest in the technical and management capabilities that enable it
- How does job satisfaction impact organizational performance?
- People do better work when they:
- Feel supported by their employers
- Have the tools and resources to do their work
- Feel their judgement is valued
- Better work results in higher software delivery performance and a higher level of organizational performance
- How does DevOps contribute to job satisfaction?
- Job satisfaction depends strongly on having the right tools and resources to do your work
- The measure of job satisfaction looks at a few key things:
- If you are satisfied in your work
- If you are given the tools and resources to do your work
- If your job makes good use of your skills and abilities
- The study found that good DevOps technical practices predict job satisfaction
- Automation matters because it gives over to computers the things that computers are good at: rote tasks that require no thinking
- This allows people to focus on the things they're good at:
- Weighing the evidence
- Thinking through problems
- Making decisions
- Being able to apply one's judgement and experience to challenging problems is a big part of what makes people satisfied with their work
- Diversity in tech - What our research found
- Diversity matters
- Research shows that teams with more diversity with regard to gender or underrepresented minorities
- Are smarter
- Achieve better team performance
- Achieve better business outcomes
- It is also important to note that diversity is not enough
- Teams and organizations must be inclusive
- An inclusive organization is one where "all organizational members feel welcome and valued for who they are and what they 'bring to the table'"
- Women in DevOps
- Women are drastically underrepresented in DevOps currently
- Underrepresented minorities in DevOps
- This group is also drastically underrepresented
- What other research tells us about diversity
- There is a great deal of research that links greater diversity to higher organizational performance
- What we can do
- It's up to all of us to prioritize diversity and promote inclusive environments
Wednesday, October 30, 2019
Intro to Kotlin, Part 2 Outline
Let's start out with showing how classes work in Kotlin. Here's a basic User class:
Enums
class User(val username: String)
fun main() {
val user = User("bob")
println(user.username)
}
- In one line we've declared a class with a property on that class
- Note that public is the default visibility
- Also note no new keyword when creating an instance
- Since val was used to define the username, you can only get the value, and not change it
- If you want to change it (e.g. have a setter), then use var
class User(var username: String)
fun main() {
val user = User("bob")
user.username = "robert"
println(user.username)
}
- Also, you can set default values for class properties
class User(var username: String = "unknown")
fun main() {
val user = User()
println(user.username)
}
- You can also define multiple classes in the same file
class User(val username: String)
class Comment(val message: String, val author: User)
fun main() {
val user = User("bob")
val comment = Comment("Hi there!", user)
}
- Classes like these that only contain data are called value objects, and are frequently known as pojos in Java
- Typically with these kinds of classes, you should implement the toString, equals, and hashCode methods
- In Kotlin, these methods can be auto implemented for you by declaring the class a data class
class User(val username: String)
class Comment(val message: String, val author: User)
fun main() {
val user1 = User("bob")
val comment1 = Comment("Hi there!", user1)
val user2 = User("bob")
val comment2 = Comment("Hi there!", user2)
println(comment1)
println(comment2)
println(comment1 == comment2)
println(comment1.hashCode())
println(comment2.hashCode())
}
compared to
data class User(val username: String)
data class Comment(val message: String, val author: User)
fun main() {
val user1 = User("bob")
val comment1 = Comment("Hi there!", user1)
val user2 = User("bob")
val comment2 = Comment("Hi there!", user2)
println(comment1)
println(comment2)
println(comment1 == comment2)
println(comment1.hashCode())
println(comment2.hashCode())
}
Let's look at something a little more complicated:
class User(val username: String)
class Comment(initialMessage: String, val author: User) {
private val msgHist = mutableListOf(initialMessage)
init {
println("Logging initial message: $initialMessage")
}
var message: String
get() {
println("Message retrieved")
return msgHist.last()
}
set(value: String) {
println("Message edited: $value")
msgHist.add(value)
}
fun compareHistory(startIndex: Int, endIndex: Int): List<String> {
return listOf(msgHist[startIndex], msgHist[endIndex])
}
}
fun main() {
val comment = Comment("hi thre!", User("bob"))
comment.message = "hi there!"
comment.message = "Hi there!"
println(comment.message)
println(comment.compareHistory(0, 2))
}
- We didn't want initialMessage to be a property, so ommitted the val/var, making it just a parameter
- Parameters can be used in the init block or to initialize properties
- Properties can have custom accessors
- Here's an example of a function in a class
- Also note that if you need more than the primary constructor, you can create secondary constructors like this:
class User(val username: String)
class Comment(messageHistory: List<String>, val author: User) {
private val msgHist = messageHistory.toMutableList()
init {
println("Logging initial messages: $messageHistory")
}
constructor(initialMessage: String, author: User): this(listOf(initialMessage), author) {
println("Secondary constructor used")
}
var message: String
get() {
println("Message retrieved")
return msgHist.last()
}
set(value: String) {
println("Message edited: $value")
msgHist.add(value)
}
fun compareHistory(startIndex: Int, endIndex: Int): List<String> {
return listOf(msgHist[startIndex], msgHist[endIndex])
}
}
fun main() {
val comment = Comment("hi thre!", User("bob"))
comment.message = "hi there!"
comment.message = "Hi there!"
println(comment.message)
println(comment.compareHistory(0, 2))
}
- You are also not required to have a primary constructor
class User(val username: String)
class Comment {
private val msgHist: MutableList<String>
val author: User
init {
println("Logging init block usage")
}
constructor(initialMessage: String, author: User) {
println("Logging initial message: $initialMessage")
msgHist = mutableListOf(initialMessage)
this.author = author
}
constructor(messageHistory: List<String>, author: User) {
println("Logging initial messages: $messageHistory")
msgHist = messageHistory.toMutableList()
this.author = author
}
var message: String
get() {
println("Message retrieved")
return msgHist.last()
}
set(value: String) {
println("Message edited: $value")
msgHist.add(value)
}
fun compareHistory(startIndex: Int, endIndex: Int): List<String> {
return listOf(msgHist[startIndex], msgHist[endIndex])
}
}
fun main() {
val comment = Comment("hi thre!", User("bob"))
comment.message = "hi there!"
comment.message = "Hi there!"
println(comment.message)
println(comment.compareHistory(0, 2))
}
- Also, you can have different visibilities for the get and set accessors
class User(val username: String)
class Comment {
private val msgHist: MutableList<String>
var author: User
private set
init {
println("Logging init block usage")
}
constructor(initialMessage: String, author: User) {
println("Logging initial message: $initialMessage")
msgHist = mutableListOf(initialMessage)
this.author = author
}
constructor(messageHistory: List<String>, author: User) {
println("Logging initial messages: $messageHistory")
msgHist = messageHistory.toMutableList()
this.author = author
}
var message: String
get() {
println("Message retrieved")
return msgHist.last()
}
set(value: String) {
println("Message edited: $value")
msgHist.add(value)
}
fun compareHistory(startIndex: Int, endIndex: Int): List<String> {
return listOf(msgHist[startIndex], msgHist[endIndex])
}
fun anonymize() {
author = User("anonymous")
}
}
fun main() {
val comment = Comment("hi thre!", User("bob"))
comment.message = "hi there!"
comment.message = "Hi there!"
println(comment.message)
println(comment.compareHistory(0, 2))
}
- And you can access the backing field for a property with the field keyword
data class User(val username: String)
class Comment {
private val msgHist: MutableList<String>
var author: User
private set(value: User) {
println("User changed from $field to $value")
field = value
}
init {
println("Logging init block usage")
}
constructor(initialMessage: String, author: User) {
println("Logging initial message: $initialMessage")
msgHist = mutableListOf(initialMessage)
this.author = author
}
constructor(messageHistory: List<String>, author: User) {
println("Logging initial messages: $messageHistory")
msgHist = messageHistory.toMutableList()
this.author = author
}
var message: String
get() {
println("Message retrieved")
return msgHist.last()
}
set(value: String) {
println("Message edited: $value")
msgHist.add(value)
}
fun compareHistory(startIndex: Int, endIndex: Int): List<String> {
return listOf(msgHist[startIndex], msgHist[endIndex])
}
fun anonymize() {
author = User("anonymous")
}
}
fun main() {
val comment = Comment("hi thre!", User("bob"))
comment.message = "hi there!"
comment.message = "Hi there!"
println(comment.message)
println(comment.compareHistory(0, 2))
comment.anonymize()
}
- You can also create infix functions
class Thread() {
val comments = mutableListOf<Comment>()
}
data class User(val username: String)
class Comment {
private val msgHist: MutableList<String>
var author: User
private set(value: User) {
println("User changed from $field to $value")
field = value
}
init {
println("Logging init block usage")
}
constructor(initialMessage: String, author: User) {
println("Logging initial message: $initialMessage")
msgHist = mutableListOf(initialMessage)
this.author = author
}
constructor(messageHistory: List<String>, author: User) {
println("Logging initial messages: $messageHistory")
msgHist = messageHistory.toMutableList()
this.author = author
}
var message: String
get() {
println("Message retrieved")
return msgHist.last()
}
set(value: String) {
println("Message edited: $value")
msgHist.add(value)
}
fun compareHistory(startIndex: Int, endIndex: Int): List<String> {
return listOf(msgHist[startIndex], msgHist[endIndex])
}
fun anonymize() {
author = User("anonymous")
}
infix fun on(thread: Thread) {
thread.comments.add(this)
}
}
fun main() {
val thread = Thread()
val comment = Comment("hi thre!", User("bob"))
comment.message = "hi there!"
comment.message = "Hi there!"
println(comment.message)
println(comment.compareHistory(0, 2))
comment.anonymize()
comment on thread
}
Visibility
- Let's touch on visibility real quick
| Modifier | Class member | Top-level declaration |
|---|---|---|
| public (default) | Visible everywhere | Visible everywhere |
| internal | Visible in a module | Visible in a module |
| protected | Visible in subclass | -- |
| private | Visible in a class | Visible in a file |
Enums
- Enums are declared via enum class, other than that they're the same as in Java
enum class Colors {
RED,
GREEN,
BLUE
}
Interfaces
- Interfaces work very similarly to interfaces in Java
interface Clickable {
fun click()
}
class Button : Clickable {
override fun click() = println("Click!")
}
fun main() {
Button().click()
}
- Note the colon used to specify interface implementation
- Also note the required override keyword
- Unlike Java, Kotlin classes are final by default. Use the open keyword to make a class inheritable
- Also, functions must be marked as open to be overridable
open class Animal {
open fun speak() {
println("...")
}
fun sleep() {
println("zzz")
}
}
class Dog : Animal() {
override fun speak() {
println("Bark")
}
}
class Cat : Animal() {
override fun speak() {
println("Meow")
}
}
fun main() {
val animal = Animal()
animal.speak()
animal.sleep()
val dog = Dog()
dog.speak()
dog.sleep()
val cat = Cat()
cat.speak()
cat.sleep()
}
- You can also use the final keyword for a final override
open class Animal {
open fun speak() {
println("...")
}
fun sleep() {
println("zzz")
}
}
open class Dog : Animal() {
final override fun speak() {
println("Bark")
}
}
class Bulldog : Dog() {
//Can't override speak()
fun growl() {
println("Grr")
}
}
class Cat : Animal() {
override fun speak() {
println("Meow")
}
}
fun main() {
val animal = Animal()
animal.speak()
animal.sleep()
val dog = Dog()
dog.speak()
dog.sleep()
val bulldog = Bulldog()
bulldog.speak()
bulldog.sleep()
bulldog.growl()
val cat = Cat()
cat.speak()
cat.sleep()
}
- You can also create abstract classes, where the base class can't be instantiated
abstract class Animal {
open fun speak() {
println("...")
}
fun sleep() {
println("zzz")
}
}
class Dog : Animal() {
override fun speak() {
println("Bark")
}
}
class Cat : Animal() {
override fun speak() {
println("Meow")
}
}
fun main() {
val animal = Animal() //This won't compile
val dog = Dog()
dog.speak()
dog.sleep()
val cat = Cat()
cat.speak()
cat.sleep()
}
- You can also create sealed classes, where subclasses are required to be defined in the same file
- This limits subclasses to a pre defined set of classes
sealed class Animal {
open fun speak() {
println("...")
}
fun sleep() {
println("zzz")
}
}
class Dog : Animal() {
override fun speak() {
println("Bark")
}
}
class Cat : Animal() {
override fun speak() {
println("Meow")
}
}
fun main() {
val dog = Dog()
dog.speak()
dog.sleep()
val cat = Cat()
cat.speak()
cat.sleep()
}
Class delegation and the by keyword
- Most experts encourage composition over inheritance, but many languages don't make composition easy
- Kotlin addresses this
interface Door {
fun enter()
}
class WoodDoor : Door {
override fun enter() {
println("Entered by door")
}
}
interface Window {
fun openWindow()
}
class ClearWindow : Window {
override fun openWindow() {
println("Opened the window")
}
}
class House : Door by WoodDoor(), Window by ClearWindow()
class Shed(door: Door) : Door by door
class WindowDisplay(window: Window = ClearWindow()) : Window by window
fun main() {
val house = House()
house.enter()
house.openWindow()
val shed = Shed(WoodDoor())
shed.enter()
val windowDisplay = WindowDisplay()
windowDisplay.openWindow()
}
Extension functions and properties
- You can, in essence, extend classes that you don't have control over through extension functions
fun main() {
val helloWorld = "Hello World"
println(helloWorld.removeVowels())
println(helloWorld.firstVowel)
}
val vowels = listOf('a', 'e', 'i', 'o', 'u')
fun String.removeVowels() =
this.filter { it.toLowerCase() !in vowels }
val String.firstVowel
get() = filter { it.toLowerCase() in vowels }.first()
- You can also use this to give classes certain functionality only in the right circumstances
fun main() {
val helloWorld = "Hello World"
println(helloWorld.removeVowels())
println(helloWorld.firstVowel) //This won't work
MySpecialClass(helloWorld) //Inside the class it will work
}
val vowels = listOf('a', 'e', 'i', 'o', 'u')
fun String.removeVowels() =
this.filter { it.toLowerCase() !in vowels }
class MySpecialClass(str:String) {
init {
println(str.firstVowel)
}
private val String.firstVowel
get() = filter { it.toLowerCase() in vowels }.first()
}
Lambdas
fun main() {
val incrementer = { a:Int -> a + 1 }
println(incrementer(1))
}
- You define a lambda by surrounding it with curly braces
- You define the parameters on the left side of the arrow
- Lamdas can be multiline, and the result of the last statement is returned
fun main() {
val incrementer = { a:Int ->
println("Input: $a")
a + 1
}
println(incrementer(1))
}
- The Kotlin standard library has lots of predefined functions that use lambdas, such as map and filter
fun main() {
val incrementer = { a:Int ->
println("Input: $a")
a + 1
}
println(listOf(1, 2, 3).map(incrementer))
}
- You can of course pass the lambda into the method directly
fun main() {
println(listOf(1, 2, 3).map({ a:Int ->
println("Input: $a")
a + 1
}))
}
- And since it is being passed in, you can infer the parameter type
fun main() {
println(listOf(1, 2, 3).map({ a ->
println("Input: $a")
a + 1
}))
}
- And when there's only one parameter, Kotlin will provide a default parameter name that you can use, called it
fun main() {
println(listOf(1, 2, 3).map({
println("Input: $it")
it + 1
}))
}
- When a lambda is the last parameter for a method, the lambda can be moved outside of the parentheses
fun main() {
println(listOf(1, 2, 3).map() {
println("Input: $it")
it + 1
})
}
- And when a lambda is the only parameter for a method, you can remove the parentheses entirely
fun main() {
println(listOf(1, 2, 3).map {
println("Input: $it")
it + 1
})
}
- Why does Kotlin support this? Because it allows you to create constructs that look like they're part of the language. For example:
fun main() {
ifnot (false) {
println("here")
}
}
fun ifnot(conditional:Boolean, body:() -> Unit) {
if (!conditional) body()
}
- And here we also see how to create functions that take lambdas as parameters
- body is a lambda that has no parameters, so it uses ()
- It also returns nothing, so it specifies the return type Unit
- To specify parameters and return types, we can do something like this:
fun main() {
val result = 1.singleMap { it > 0 }
println(result)
}
fun <T, R> T.singleMap(mapper:(T) -> R) =
mapper(this)
- Note that parenthesis around the parameter types is always required
- We also get a small taste of generics here
- We can also return a lambda from a function, like this:
fun main() {
val add1 = curry(::add, 1)
println(add1(2))
}
fun add(a:Int, b:Int):Int = a + b
fun <T, U, R> curry(function:(T, U) -> R, t:T): (U) -> R {
return { u ->
function(t, u)
}
}
- Also note that we can pass regularly defined functions as lambdas by prepending them with ::
- You can also inline functions, and the compiler will inline the code with the lambda at compile time
fun main() {
ifnot (false) {
println("In ifnot")
}
}
inline fun ifnot(conditional:Boolean, body:() -> Unit) {
if (!conditional) body()
}
- This allows for some speedups, since lambda objects don't need to be created at runtime
- Note that an inlined function cannot call and pass its lambda to another function unless that function is also inlined
- It also allows you do to some extra things in your lambda, such as using a return statement
fun main() {
ifnot (false) {
println("In ifnot")
return
println("After return") //It won't reach this code
}
println("After ifnot") //It won't reach this code, either
}
inline fun ifnot(conditional:Boolean, body:() -> Unit) {
if (!conditional) body()
}
- Note that if you wanted to just return from the lambda, you can do so as follows:
fun main() {
ifnot (false) {
println("In ifnot")
return@ifnot
println("After return") //It won't reach this code
}
println("After ifnot") //But it will reach this code
}
inline fun ifnot(conditional:Boolean, body:() -> Unit) {
if (!conditional) body()
}
- You can also specify a label, instead of using the method name:
fun main() {
ifnot (false) marker@{
println("In ifnot")
return@marker
println("After return") //It won't reach this code
}
println("After ifnot") //But it will reach this code
}
inline fun ifnot(conditional:Boolean, body:() -> Unit) {
if (!conditional) body()
}
- You can also create lambdas with receivers, which bind the this keyword to something specific in the lambda
- You can think of them as extension functions as lambdas
- Here's how the with statement in the standard library works (note that the standard library with statement has a little more to it)
fun main() {
val result = with("Hello World") {
substring(6)
}
println(result)
}
inline fun <T, R> with(receiver:T, block:T.() -> R): R {
return receiver.block()
}
Tuesday, October 29, 2019
Accelerate Chapter 9 Discussion Points
Chapter 9 of Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations, is about making work sustainable.
- Burnout and deployment pain are important issues to address
- Deployment pain
- Highlights the friction and disconnect that exist between
- The activities used to develop and test software and
- The work done to maintain and keep software operational
- This is where development meets IT operations
- Where code deployments are most painful, you'll find the poorest software delivery performance, organizational performance, and culture
- It is also a concern when development and test teams have no visibility into code deployments
- If they aren't aware of the deployment process, there are probably barrier hiding the work from them
- Barrier that hide the work of deployment from developers are rarely good, because they isolate developers from the downstream consequences of their work
- Teams reduce deployment pain when they improve key technical capabilities, such as:
- Implement comprehensive test and deployment automation
- Use continuous integration, including trunk-based development
- Shift left on security
- Effectively manage test data
- Use loosely coupled architectures
- Can work independently
- Use version control of everything required to reproduce production environments
- The technical practices that improve our ability to deliver software with both speed and stability also reduce the stress and anxiety associated with pushing code to production
- There's a high correlation showing that the more painful the code deployments are, the poorer the IT performance, organizational performance, and organizational culture
- Most deployment problems are caused by a complex, brittle deployment process stemming from these factors:
- Software is often not written with deployability in mind
- Manual changes must be made to production environment as part of the deployment process
- Require multiple handoffs between teams, particularly in siloed organizations
- To reduce deployment pain:
- Build systems that:
- Are designed to be deployed easily into multiple environments
- Can detect and tolerate failures in their environments
- Can have various components of the system updated independently
- Ensure that the state of production systems can be reproduced (with the exception of production data) in an automated fashion from information in version control
- Build intelligence into the application and the platform so that the deployment process can be as simple as possible
- Applications designed for a platform-as-a-service can typically be deployed using a single command
- Burnout
- Deployment pain can lead to burnout if left unchecked
- It is physical, mental, or emotional exhaustion caused by overwork or stress
- It can make things we once loved about our work and life seem insignificant and dull
- It often manifests as a feeling of helplessness, and is correlated with pathological cultures and unproductive, wasteful work
- Research shows that stressful jobs can be as bad for physical health as secondhand smoke and obesity
- Stress costs the US economy $300 billion per year in sick time, long-term disability, and excessive job turnover
- Organizations can fix the conditions that lead to burnout by
- Fostering a supportive work environment
- Ensuring work is meaningful
- Ensuring employees understand how their own work ties to strategic objectives
- Managers should concentrate on:
- Fostering a respectful, supportive work environment that emphasizes learning from failures rather than blaming
- Communicating a strong sense of purpose
- Investing in employee development
- Asking employees what is preventing them from achieving their objectives and then fixing those things
- Giving employees time, space, and resources to experiment and learn
- Common problems that can lead to burnout
- Six organizational risk factors that predict burnout:
- Work overload: job demand exceed human limits
- Lack of control: inability to influence decisions that affect your job
- Insufficient rewards: insufficient financial, institutional, or social rewards
- Breakdown of community: unsupportive workplace environment
- Absence of fairness: lack of fairness in decision-making processes
- Value conflicts: mismatch in organizational values and the individual's values
- Most organizations try to fix the person and ignore the work environment, even though fixing the environment has a higher likelyhood of success
- How to reduce or fight burnout
- Five organizational factors that are highly correlated with high levels of burnout:
- Organizational culture
- Managers are responsible for fostering a supportive and respectful work environment
- They can do so by
- Creating a blame-free environment
- Striving to learn from failures
- Communicating a shared sense of purpose
- Remembering that human error is never the root cause of failure in systems
- Deployment pain
- With the right practices in place, deployments don't have to be painful events
- Managers should ask their teams how painful their deployments are and fix the things that hurt the most
- Effectiveness of leaders
- Responsibilities of a team leader include limiting work in process and eliminating roadblocks for the team so they can get their work done
- Organizational investments in DevOps
- Organizations that invest in developing the skills and capabilities of their teams get better outcomes
- Investing in training and providing people with the necessary support and resources (including time) to acquire new skills are critical to the successful adoption of DevOps
- Organizational performance
- Lean management and continuous delivery practices improve software delivery performance
- At the heart of Lean management is giving employees the necessary time and resources to improve their own work
- This means creating a work environment that:
- Supports experimentation, failure, and learning
- Allows employees to make decisions that affect their jobs
- Creating space for employees to do new, creative, value-add work during the work week, and not just expecting them to devote extra time after hours
- By aligning organizational values with individual values, employee burnout can be reduced
Subscribe to:
Posts (Atom)