Below is an article originally written by Elizabeth Giles at PowerToFly Partner PagerDuty, and published on January 8, 2020. Go to PagerDuty's page on PowerToFly to see their open positions and learn more.
In a story that will sound all too familiar to many developers, in early 2019, I picked up a Kanban ticket to update a legacy system's documentation—a microservice so old that no one on my team really knew much about it. In this blog, I'll share the lessons I learned from familiarizing myself with a legacy system and the positive outcomes of doing so. Additionally, I'll share some steps you can take to look at some of your own legacy systems—if you dare.
When I picked up the ticket, my team was preparing to pass on ownership of the service—one of our oldest and most neglected—to a team that had plans to give it some attention. There were just two things standing between us and changing the PagerDuty escalation policy associated with that service: ensuring that its documentation was up-to-date and having a knowledge transfer session with the new owners.
These were actually bigger hurdles than they sound due to the general lack of knowledge about the legacy service. Nevertheless, I was the developer who ended up with the Kanban ticket to update the service's documentation.
How Did We Get Here?
But how did our team even get to this point to begin with? The service in question was roughly 15,000 lines of Scala code built around 2015 by a completely different team. Over time, the developers who had originally built the service moved on from PagerDuty to new opportunities. Once my team inherited it, we rarely had to touch it for tasks larger than updating some of our tooling.
You see, my team owned a fairly long list of services, many of which we were doing active development on as part of building new features and scaling existing ones. It wasn't a priority for us to devote attention to a service that wasn't involved in any of our new features, rarely caused us operational pain, and wasn't anywhere near the top of our list of scaling bottlenecks.
And then, those from my team who had been around for the original ownership transfer and had done any feature development on the service also moved on. Eventually, we were left with developers who had touched the service a handful of times at best and weren't particularly comfortable with Scala since PagerDuty uses mostly Elixir now. We knew the basics of the service, sure, and we were more familiar with it than folks on other teams, but no one was particularly confident in their knowledge of the details.
(If you're worried after this post that we never go back and improve our legacy systems here at PagerDuty, check out our posts about centralizing scattered business logic in our Elixir webhook service and revisiting our Android architecture.)
Steps to Dive Into an Unfamiliar System
In order to make sure that the documentation was up-to-date and accurate, I did a deep dive to try to resolve some of the gaps in my knowledge.
There are many tips for understanding large, unfamiliar codebases out there (I find Michael Feathers' book, Working Effectively with Legacy Code, particularly helpful), and everyone develops their own techniques, but the steps below are the ones I took to tackle this challenge and could be applied if you're in a similar situation.
Step 1: Reading the Feature Documentation
As developers at PagerDuty, we're fortunate to be in a position to use our own product on a regular basis; however, that doesn't always mean that I'm an expert in all of our feature sets. Because of this, I started by reading the public documentation available for the features this service was powering. This pre-work reading helped me understand the intent and possible edge cases that led to the creation of the code I would end up reading.
Step 2: Tracing Requests
When I was ready to start diving into the codebase, I began by giving myself logical paths to follow by identifying where HTTP requests and other inputs entered the system and tracing through what happened with them from there. I then created a flow diagram so that I didn't have to rely on memory to keep track of everything.
Step 3: Examining the Data Lifecycles
Many services accept multiple types of requests that all interact in different ways with data that the service is storing. In these cases, it can be difficult to get a good view of how the entire system works when tracing one type of request at a time. To address that, after tracing individual requests, I took a step back and focused on the pieces of data being stored to understand their lifecycles—how they were created, updated, accessed, and deleted.
Step 4: Refactoring
Once I gained a better understanding of the system, I checked those assumptions by trying to make changes to the code, usually some refactoring, to try to make it more comprehensible. If the changes seemed to work, and didn't cause compiler errors, break tests, or cause the system to behave in a way that didn't match its feature documentation, I could be fairly confident that my assumptions were valid.
Often throughout this process, I was making changes purely for learning purposes with the full intention of reverting them afterwards. This meant that I could move faster and focus on understanding the service rather than following all conventions or what level of risk I was willing to take with the new changes.
Step 5: Explaining the Service
Once I felt fairly confident in my understanding, the last thing I did was to try and explain how the service worked—sometimes to another person and sometimes to a rubber duck. Either way, questions or gaps would come up that made me realize there were still parts of the service I needed to investigate further.
When You Learn More Than You Expect
By the end of the above process, I had a much better understanding of how the service actually worked as well as a good start on the documentation that I needed to write.
The service in question was used to enable some of PagerDuty's many integrations. Essentially, it stored information about different actions to be taken for each integration and provided interfaces for accessing and updating those actions.
This simplifies the real service quite a bit, but as an illustration, I ended up with some notes like this to describe how it handled requests:
- Fetch the metadata for the current version of the integration from the database.
- Insert a new entry into the database with metadata for the new version, with an incremental version number.
- Store the full set of actions for the new version of the integration in the object store system under the path included in the database entry.
- Mark the new database entry as "active."
- Mark the database entry for the previous version as "not active."
- Fetch the metadata for the current version of the given integration from the database.
- Use the path included in the metadata to fetch the full set of actions for the integration from the object store system.
And thus, I ended up with notes like this to describe the lifecycle of a row of the integration metadata table being stored in the database:
- INSERT: Row 55 is inserted into the database as a result of a request to update integration #3.
- UPDATE: The
activecolumn of row 55 is updated to
true, after the full actions set from the request has been stored in the object store system.
GET /3/actions (any number of times)
- SELECT: The data in row 55 is returned as a result of a query to the database for the most recently created entry with
- SELECT: Row 55 is returned as the current integration version as a result of a query to the database to find an entry with
- UPDATE: After the new version of the integration is completely deployed, the
activecolumn of row 55 is updated to
This wasn't a substitute for having built the service or done substantial work on it myself, but it was a fairly quick process that enabled me to come to a stronger understanding of the service and opened my eyes to a few unexpected realizations.
A Bug Appears!
Let's go back to the lifecycle of a database row for a minute.
You may have noticed, as I did, that the service had two different ways of fetching the "current" version of an integration from the database, based on two similar sounding columns,
active. This came as a result of gradual additions of features to the service and gradual evolutions of the data model to support them. I promise that it's much easier to see in the simplified version of the service that I've described here than it was in reality.
When I did notice this, I realized that I had inadvertently identified the source of a long-running annoyance for my team.
We had been aware for some time that this service would briefly return error responses to
GET requests for a specific integration while a new version of that integration was being created. If we were ever notified about an incident on the service during an integration deploy, we knew that it would most likely be transient and nothing to worry about.
However, it had never been worth our time to investigate the issue due to our lack of familiarity with the service, the fact that errors were retried so there was no customer impact, and the general rarity of new integration deploys that only ever happened during business hours anyway. So we just treated it like one of the unavoidable quirks that you tend to find in legacy systems.
In my dive into the service, I discovered that those errors were occuring because of its multi-step process for responding to
POST requests, where a new row containing integration metadata was first inserted into the database and then the full set of actions corresponding to the new integration version was persisted in our object store system. When
GET requests were received by the service between the database insertion and the persistence of the actions, the service wouldn't be able to find actions in the object store system under the
path specified in the database for the "current" version of the integration.
This meant that the operational noise we had been seeing was actually very easily avoidable and wasn't some complex issue inherent in the design of the service. We were already setting the
active column for a row in the database to
true only when the actions associated with that row were fully persisted, and were using that column elsewhere as our method for identifying the "current" integration version. So if we adjusted GET requests to also query the database only for rows with
active=true, the errors during deploys would be eliminated.
I took a couple of hours to do just that during our next Hackday, and we haven't had problems since!
What Learning a Legacy System Can Do for You
The experience of diving deeper into this particular legacy system has helped me recondition how I think about legacy systems in general.
I'm certainly not going to argue that it's vital for developers to always be equally familiar with all of the services that they own, including legacy ones, to the point of getting into a cycle of rewrites every couple of years. But I feel more strongly now about a middle ground, where if a team has a general lack of confidence in their understanding of a system, it could be valuable to take a day or two to try to fix that.
When learning about your legacy systems:
- You can often identify quick improvement wins when you're looking at a system with fresh eyes from a holistic perspective (like I did in this example). Even if you can't make the improvements right away, it can be nice to have a shortlist of them sitting around to work on when you have gaps between larger projects.
- You can gain a better understanding of which parts of the system are most likely to break and how soon those failures might happen, which can be useful when planning for the future. Even if you have no immediate plans to make changes, no system fulfills its purpose and scales forever without some form of attention.
- If you've decided that you're going to be completely replacing your legacy system, you can get a head start on figuring out the challenges of the problem space and identifying edge cases in behavior that you will want to take into account in your replacement system.
Understanding how a legacy system works isn't likely to be the biggest challenge involved in owning it. But making a small investment in that area will help you with any of the other challenges that you end up facing.
Do you have opinions and/or horror stories about dealing with legacy systems? Or perhaps a favorite process for understanding how an unfamiliar system works? We'd love to hear from you in our Community forums.
Katie Dillon has many hobbies. During the pandemic, she picked up candle and jewelry making, opened an Etsy shop, learned new watercolor techniques, and poured hours into maintaining her vegetable garden.
And recently, her interest in the lindy hop community has been resparked. “Swing dancing is something I enjoy doing,” she shares. “I used to travel for dance every other weekend. It was a huge part of my life. And I recently got inspired to get back into it.”
Whether through crafting or dancing, Katie enjoys harnessing her creativity — a skill she also uses for her work as a Software Engineer at SeatGeek. We sat down with Katie to learn how producing effective code involves creativity and design thinking.
Following Her Interest in Design
Katie grew up within a family of software engineers. “My dad is a software engineer and my younger brother has always wanted to be a software engineer,” she shares.
Katie, on the other hand, wanted to carve her own path. “I wanted to do the absolute farthest thing that I could think of from software engineering. There was no way that I was going to code.”
In an effort to find her own voice, she joined a filmmaking program in high school. “It was film, design, and English,” she explains.
After two years in the program, her mind was set on filmmaking and she applied to several university filmmaking programs. Although she was accepted to some reputable schools, she started having second thoughts.
"I thought I didn't know exactly what I wanted to do, so I wanted to go to a school that allowed me the flexibility to change my mind. I ended up applying to some design schools and then going to the University of Michigan," she says.
There, she pursued an art and design major and started on her career journey.
“I [ended up] doing graphic design, UI/UX stuff,” she explains. “I was doing a lot of freelance design work and consulting for small businesses. I was full design and felt pretty good about that for my future.”
Katie had regular clients and a full schedule with her design work, however, she felt inclined to take an intro to coding class to stay current—and keep up with her family’s dinner table conversations about machine learning.
“[I thought to myself], ‘I'd like to understand what this chaos is when my dad talks about it,’” she admits with a smile.
And after that first class, she was hooked.
Merging Creative Design With Coding
“I took one class and I [knew] this was for me,” Katie shares.
“It opened my eyes to the fact that engineering can feel like adult Legos, where it's highly creative, but in a way that also tickles my organization brain,” she explains.
Because of her newfound interest, Katie decided to finish her design degree with a minor in computer science. While working to achieve this, she got a first glimpse at what a career in tech could look like. This glimpse came from an internship for a company she was previously doing design work for. “It was a local company in Ann Arbor. [I told them] I wanted to code and it worked out great,” she shares. She went on to describe the invaluable mentorship and support she received during her transition from design to code. “That internship really helped me envision what it would look like to work as a software engineer,” she adds. “Something I’m still grateful for.”
Because of her design background, Katie was able to draw similarities between designing and coding. From a design perspective, coding is “designing how a system is going to work or designing the flow of information,” she explains.
She has always thought of design as a form of creative problem solving; understanding a problem or a pain point that needs to be solved, ideating different possible solutions, and then realizing those solutions.
Similarly, coding involves designing creative solutions to problems. In both cases, these problems often have many solutions. “With coding, we're not outputting something visual, but designing how information moves through a system,” she explains.
The key is being able to design code that helps reach goals; and design thinking plays a crucial role in that. “There are so many different design choices that make good code.”
Using Creativity to Code at Seatgeek
After her first experience with coding, Katie decided to expand her career and found SeatGeek through a job search. What caught her attention was the staff.
“Something that resonated with me was that there were these people in all different walks of life who, I felt, SeatGeek honored and encouraged to be their whole [selves] both inside and outside of work,” Katie says.
SeatGeek is the live entertainment platform that’s rethinking ticketing by caring more about fans, teams, and venues. With their technological savvy and fan-first attitude, they’re simplifying and modernizing the ticketing industry.
Now as a Software Engineer, Katie uses creativity and design work in her coding process. “I use creativity more when I’m thinking about and planning code,” she adds.
“[On my team] we try to think about these big problems and break down those problems into smaller chunks and that process is so creative to me. We’re figuring out what needs to be solved and then designing some sort of solution.”
Advice on Using Creativity to Power Your Code
Creativity is a beneficial skill — one that Katie uses on a regular basis.
“In my job, I end up wearing many hats and playing designer when writing frontend code,” she explains. “It's always great when I'm able to collaborate with someone and have explicitly asked for design input on bigger projects, but when that's not possible, my design background allows me to still be effective and create user-friendly interfaces through conversations with stakeholders and an iterative design process.”
Katie emphasizes that everyone should identify their own creative processes and harness those when designing and writing code, but she offers this advice for those searching to vary that creative spark:
- When in doubt, draw it out. “This may not work for everyone but it works for me to have a physical pen to paper and be able to draw my ideas,” says Katie. “Whether you’re drawing a diagram or a doodle, it doesn’t have to be perfect. This process can reveal the weak points and help you focus and iterate on your ideas.”
- Be open to collaboration. Having open and casual meetings with other engineers can create the space for innovation. “I think that some of the most effective and groundbreaking meetings don't really have a plan other than ‘let's talk about this big idea and think about it,’” Katie shares. “Talking to other engineers during that unstructured design time is really helpful.”
- Do the big design work first. “Doing enough of the planning and design work ahead of time, I feel, lays the base to be more creative with the small things,” shares Katie. “Once you have the structural pieces in place, you can utilize creativity by getting feedback and bouncing ideas off of other colleagues to fill in the missing components.”
If you’re ready to apply creativity and design to solve big problems, check out the open positions at SeatGeek.
We all have our favorite websites– the ones we frequent, bookmark, and recommend to others. You might even enjoy some website features so much that you’ve found yourself wondering why they aren’t more popular. Or maybe you’ve experienced times where you were frustrated with a website and wished you could add features or even design your own!
If you’ve ever found yourself intrigued at the prospect of designing and developing your own websites, then a career as a web developer might be just for you!
As a web developer you would be responsible for coding, designing, optimizing, and maintaining websites. Today, there are over 1.7 billion websites in the world and, in turn, the demand for web developers is on the rise. In order to figure out what kind of web development work best suits you let’s start with an introduction to the three main roles in web development that you can choose from.
The Three Types of Web Development Jobs
Front-End Web Development: The Creative Side
In addition to programming skills, front-end developers need to be detail oriented, creative, willing to keep up with the latest trends in web development, cyber security conscious, and geared toward user-friendly designs. The median salary for a front-end developer can reach well into the $90,000 to $100,000 range.
Back-End Web Development: The Logical Counterpart
While a house can be beautifully decorated, it’s incomplete without a solid foundation and efficient infrastructure. Similarly, a well-designed website depends on logical and functional code to power the features of that website. Back-end web development is code-heavy and focused on the specifics of how a website works. If you enjoy the analytical challenge of creating the behind-the-scenes code that powers a website, then back-end development is for you.
Full-Stack Web Development: A Little Bit of Everything
A full-stack developer is essentially the Jack (or Jill)-of-all-trades in web development. Full-stack developers need to be knowledgeable about both front-end and back-end roles. This does not necessarily imply that you would need to be an expert in both roles, but you should fully understand the different applications and synergies they each imply. In order to work in this position, you will need to know the programming languages used by front-end and back-end developers. In addition to these languages, full-stack developers also specialize in databases, storage, HTTP, REST, and web architecture.
Full-stack developers are often required to act as liaisons between front-end and back-end developers. Full-stack developers need to be both problem solvers and great communicators. The end goal for a full-stack developer is to ensure that the user’s experience is seamless, both on the front-end and on the back-end. In return, you can expect to earn a median salary of $100,000 – $115,000 a year for this role.
Taking the Next Step
Web development is both in-demand and lucrative! All three roles described above contribute to specific aspects of web development and the scope of each one can be customized to the industries and positions you feel best suit you. Regardless of which role you choose, all of them need a foundation in programming.
To gain the programming skills needed in each role, you can enroll in courses or learn independently. Coding bootcamps are a great way to boost your skillset quickly and efficiently.
Click here for some of our highly rated programming bootcamp options! Make sure to check out the discounts available to PowerToFly members.
Editor’s note: The following post is by Shweta B., Vertical Head for Financial Services, Media, Travel and Professional Services, in India. In her own words, she describes four core characteristics that empower client partners and client solution managers on the Global Business Group team to succeed.
When I’m asked about the last two years of my career journey, the first thing that comes to mind is the people I’ve worked with and the impactful change we’ve made together. After four years in business operations at another tech company, I started thinking about how I could apply my leadership skills to a new challenge. After exploring a few different opportunities, I was introduced to a position with the Global Business Group team in India that aligned with my experience and interests in developing a team of client partners and client solution managers. I was struck by something else I discovered through my interactions with my Meta colleagues as well: an incredibly inclusive culture where it was clear that the team was intentional about empowering one another to grow. Collaborating with a diverse group of people from unique backgrounds has been one of the most rewarding parts of my experience over the last two years with the company. I’ve also been inspired by observing how, despite the different paths people on my team have taken throughout their career journeys, we all share several key traits and skills: empathy, curiosity, authenticity, and the ability to think ahead. Looking back, I’ve reflected on how each of these characteristics are helping to power our collective success.
Empathy not only helps me connect with my team, but it empowers us to build relationships with one another, the cross-functional partners we work with internally, and our clients - advertisers across the travel, financial, media, and professional services industries. Strong relationships enable us to establish a foundation of trust, facilitate open and honest conversations, and see things from a perspective outside of our own. This is especially important for my team. Our clients depend on us to advise on their advertising strategy, and in order to help them meet their goals, it’s critical that we understand who they are, what they’re doing, and what matters most to their customers. With this, we must also recognize that there isn’t a “one-size-fits-all” approach. What works for one client won’t necessarily work for another, and solving problems often means working together to think about a creative idea we haven’t tried before. Having deep care for the end-user and being empathetic about their needs is a north star that helps us continuously strive for building the best experience possible.
Shweta and the GBG In Market team enjoying time together at the Meta office in Gurgaon, India.
At Meta, we’re on the frontier of inventing cutting-edge ways to develop new advertising solutions—and with that, we’re implementing methods we haven’t tried before. Innovation is a key element of our team and company culture, and introducing new ideas calls for deep curiosity. Rather than doing things as they’ve always been done, people who thrive on my team challenge each other to be bold, think bigger and share outside-of-the-box ideas. We ask ourselves questions like, “What can we do for the client if we think beyond product or bandwidth constraints”, “What could this look like in the long-term?”, and “We might not have the skills internally to answer all of our client’s questions, but who can we partner with cross-functionally—or globally—to solve this problem?” As the first point of contact for clients, bringing our curiosity to our cross-functional teammates is incredibly valuable. We ask questions to introduce new ideas, and we collaborate closely to solve challenges.” Curiosity also means being open to change, and it’s a trait I look for when meeting with potential team members. Outside the questions focused on skills, I usually ask potential candidates for their honest view on Meta technologies, what do we do exceptionally well, and where we could do better as a company from their perspective. While we hire for specific roles, we leave space for people to help redefine their roles and take on new challenges as our work evolves. This not only empowers team members to employ their curiosity to explore different interests and possibilities, but to think more broadly about how to make the most meaningful impact.
Authenticity—bringing your full self to work—makes it possible to be open, transparent, and vulnerable. In turn, we can communicate more effectively, foster strong relationships and lead by example. Having these capabilities is like having superpowers when collaborating cross-functionally, developing a team, and working with clients. Despite the incredible benefits of being authentic, I wasn’t always certain how to balance being my full self and showing up as a strong leader. I initially felt hesitant to share details about my life or talk about what mattered to me outside of work—and that prevented my team from being more open with me. At Meta, being authentic is at the heart of our culture. Leaders embrace transparency and demonstrate what it means to be open. Experiencing this has inspired me to rethink my own approach and grow. “When we’re comfortable being our authentic selves, we’re most empowered to put our best foot forward.” Now, one of my favorite parts of the week is the first 15 minutes of our weekly team meeting. We talk about anything and everything—as long as it’s not work-related! This bonding time helps us learn more about one another. I’m proud of the relationship we’ve formed together, and they know I truly care about each of them. This comfort and connection extends to our work with clients as well, helping us bring a transparent, people-first approach to our work. We won’t suggest a product that doesn’t align with their goals just to fulfill ours, and we’ll be realistic when we need to think about changing course. While these authentic interactions may seem small, they make a big impact.
No two days are the same here, and we’re constantly thinking about what’s next. No matter a team member’s experience or role, having the ability to look ahead enables us to keep the big picture in mind and work toward longer-term goals. It also inspires us to be creative and start conversations about how small decisions today can contribute to what happens tomorrow.Right now, we have a massive opportunity to shape the future and make an impact across India in the travel, financial, media, and professional services industries. I often think about how this is the first time that India’s digital advertising industry has surpassed the TV industry, and how WhatsApp and Instagram have exploded in popularity over the last few years. The work we do with our clients is part of this shift in the way people consume advertising and media, and everyone on the team is able to drive change for the community. While we’ve already accomplished so much together, there’s still limitless opportunity to look forward to!
Tiffany Witwer from Elastic is a proud mom of three.
“I enjoy being a parent because it teaches me patience and it gives me a different perspective,” she shares. “It allows me to be more present, laugh more, and appreciate the small things.”
In between her duties as a mom, she keeps herself mentally and physically healthy by running, biking, swimming, or doing yoga — all activities that help her start the day with gratitude. "It gives me the right perspective and attitude to go into the day,” she says.
With an overall positive outlook on life, Tiffany brings that same energy to her customers at work as the Head of Customer Service for Elastic.
We sat down with Tiffany, who shared with us her career journey from civil engineering to customer service. Keep reading to learn top tips for creating happy customers.
Starting a Career in Engineering
Tiffany pursued an undergraduate degree in biological engineering.
“I was always really good at math and science, especially chemistry. And I love being outside in nature and learning about it,” she shares.
It was a college professor’s research on stormwater runoff that motivated her to pursue her master's degree in biological and civil engineering. “I liked his energy and attitude toward learning. It was contagious,” she describes.
While working alongside this professor at North Carolina State University, she presented her work at a conference that helped lay the groundwork for her career. “I met a man who liked my presentation," she says, "and was hiring a civil engineer for a consulting company.”
Taking on this new opportunity, she moved to New York City where she discovered her love of being surrounded by diverse people and cultures, in addition to her new job.
“I enjoyed doing the design work and meeting the customers,” she explains. "I was always the one on the proposals, winning the design work, and building relationships with customers.”
While emerging in the complex realm of storm waste engineering, Tiffany saw how the world was progressing and thought that knowing software and technology would be beneficial.
“So I learned to code, networked, and got a job at a business analytics and software company as a pre-sales systems engineer,” Tiffany says.
Pivoting into a Customer Success Role
As she dedicated more time to customers, her interest in working with them soon began to increase. “What I loved most was that I was using my mind to solve problems, but I also got to interface with customers. I got to meet customers and hear what they were doing and hear how we could help them.”
Tiffany spent 10 years in pre-sales engineering and sales. She then took a job in a different company where she helped build out their advisory services business.
It was there that she built a successful team with coworkers who would lead her to her position at Elastic.
Elastic is the leading platform for search-powered solutions. They help enhance customer and employee search experiences, keep mission-critical applications running smoothly, and protect against cyber threats.
As the Head of Customer Service, Tiffany is responsible for making sure customers are getting the most value out of their software. "It's not only about how customers are using the technology," she explains. "It’s, ‘how is a customer's experience with Elastic? Are we meeting their need for technology?’ And, ‘are we meeting their needs from a support and empathy standpoint?’”
In order to meet her customers’ complex needs, she emphasizes how crucial communication is.
The Importance of Communication in Customer Success
Quality communication is a skill that can often be undervalued. “I think people underestimate how much time is needed for clear communication,” she points out. “Just because you put a message out there, it doesn't mean it’s clearly understood. You need to think through how people are going to respond to the information.”
With the complexities of communication, Tiffany relies on setting clear intentions when communicating in meetings. “I always ask at the beginning, ‘what is your goal for this meeting and what does success look like for you?’" she explains.
Communicating clearly what success looks like for both parties allows for a better outcome. “I think for communications, it's making a lot of time and clearly defining what you want to get out of the interaction.”
Advice for Clear Communication with Customers
Tiffany’s career journey has been a mixture of understanding technology and building relationships with people — learning how to explain the technology to customers and problem solve in an empathic way. This has led to overall customer success. To create clear communication, Tiffany offers this advice.
- Be empathetic and listen to your customers: “If you think about it, you've been trained in your technology, you know it inside and out,” she explains. "But when you meet with a customer, the technology may only be a small part of their job.” Taking this perspective can help you to communicate with more empathy. “It's understanding people's vantage point and then using that to communicate to them.”
- Defining success and clearly communicating it: “I'm a strong believer in getting on calls and confirming the goals and what people want to get out of the call," Tiffany shares. "This way, you know, you are aligned on what success is no matter what type of call.”
- Be genuine: “At the end of the day, people will remember how you made them feel," she shares. "I think for me, it's about being a good human and making the world a better place. And if you can do that in your job as well, that's a win-win.”
- Get to know people: “Getting to know people, their perspectives, and growing with them is what has led me to customer success and to where I am in my career,” Tiffany advises.