Why You Should Let Your Best Employees Join Another Team

Another Team

Imagine a fantastic software engineer who’s been working for the same team on the same system for a few years. Before long, she gets promoted. Her colleagues and direct reports love her, but despite getting more responsibilities, she’s growing tired of the same set of technical challenges.

A team is being put together elsewhere in the company in order to build a new system, and she has her eye on that. So she decides to approach her manager about it.

WHY INTERNAL TRANSFERS ARE SO RARE

At many companies, this type of request isn’t greeted too enthusiastically. Plenty of organizations have official policies on internal transfers, but they’re seldom utilized as much as they could be. I once had a manager who would say that a team member of theirs “quit” when they transferred, and actually tried to prevent that from happening. A colleague of mine says he once got scolded by a manager who found out he was using the internal jobs site, and was then “forgiven.” In these situations, company culture undermines company policy.

PLENTY OF ORGANIZATIONS HAVE OFFICIAL POLICIES ON INTERNAL TRANSFERS, BUT THEY’RE SELDOM UTILIZED AS MUCH AS THEY COULD BE.

Explicitly or implicitly, when the opportunity to apply your skills in a different part of the company is discouraged, you’re more likely to jump ship altogether.

For managers, though, it comes down to weighing the costs and benefits. If you discourage or outright reject the transfer request and keep that team member on board, you will:

  • Have a demotivated and unhappy employee on your hands.
  • Deliver a clear message to the rest of your team that there’s no leaving this group; there’s only leaving the company.
  • Eventually—usually within six months—be handed a resignation letter.

If you say yes, you’ll no longer have the great employee on your team, but will:

  • Have the opportunity to negotiate when they can transfer (e.g. one or two months, which is far better than two weeks’ notice), so you’ll have enough time to recruit the right replacement and bring them fully up to speed.
  • Deliver a clear message to the rest of your team that you can have a career here and not just a job.
  • Retain a talented and happy employee who keeps using their expertise on behalf of the company.

GIVE THE GIFT OF GROWTH

Some companies actually excel at letting their top employees move around within the organization in order to develop their skills. GE, for instance, has been known to approach top performers who’ve been in their roles for around one-and-a-half to two years and ask about their next position in the company. The point is to get the best people to stick with GE for the long haul and develop a career there.

For employees, the question is when, whether, and how to ask managers about the possibility of transferring to another team. Some companies don’t require you to do that, but some do. Regardless, those conversations should happen, and managers should encourage them. The ideal manager should be a partner to employees, not someone to hide from. Interest in working elsewhere within the company should never be a dirty secret or interpreted as a critique of a manager’s leadership style.

EMPLOYEES WHOSE CAREERS YOU’VE HELPED ADVANCE WILL REMEMBER THAT

Personally, I’ve had two people approach me about transferring to other groups during the past six months. One person wanted to increase the breadth of their experience, and the other wanted to be in the group focused on services (back end is their passion). Both of those employees brought a lot to my team. But after talking it out with them individually, I said yes to both. I knew they’d be able to grow and follow their passions, and it was my job to encourage that.

Sometimes managers are in a tough spot. What should you do if you’d like to help your employees grow in a different part of the company, but the organization frowns on that? Some managers’ judgment will be questioned if they let top performers leave. That risk is real, but it’s better to err on the side of doing right by your employees.

If you do, you’ll ultimately be doing right by your company, too—even if it isn’t exactly seen that way. The reality is that people now change their employers more often than ever, so minimizing that churn can be a good thing. What’s more, employees whose careers you’ve helped advance will remember that. You may be losing someone great now, but they might come back into your professional life sooner than you’d expect.

Post originally appeared in Fast Company.

The New Ladders Engineering Blog

Fritz

Welcome All,

Here at Ladders, we’ve been busy with a lot of cool efforts:

  1. We’ve changed our name from TheLadders to Ladders.
  2. We’ve started redesigning our website with a new look and feel.  Our design team has been amazing!
  3. We’ve pushed forward our tech stack with ReactJS & Flux.
  4. We’re building some fantastic new tools such as the Resume Reviewer.
  5. We’ve welcomed a lot of awesome new engineers who will help us build some fantastic new products – and have a fun time doing it!

In commemoration of all of our changes, we are launching a new Ladders Engineering Blog with our new mascot Fritz (that’s him above).  Our mission: Making Engineering Awesome!

Going forward, we’ll bring you regular updates of what our Engineering team is up to, including our successes and failures (hey, failing fast is good if you learn from it).

Stay tuned, and if you’re interested in joining an awesome engineering team, check out our openings.

Regards,

Geoff Bourne, CTO @ Ladders

Managed Chaos: Empowering Your Engineering Team

I believe in the software engineering team using COBOL and Fortran ’77 (so much better than ’66) to develop the best modern websites.  I believe in them using note cards and excel files and Jira and Trello to organize the Agile Product Backlog, and using nothing at all.  I believe in the teams having a leader and having no leader.  I believe in what the team wants to do.

While a bit tongue-in-cheek, I truly believe that a successful, innovative, and evolving organization must encourage its members to decide how they get the job done.  Trust the team to find what works best to achieve their goals, which of course should be aligned with the company goals.  The effective leader provides vision, values, and transparency.  The leader must protect the team by setting up the metaphorical guardrails by the cliff’s edge, but between those guardrails create an open and fertile environment for the teams, who are professionals and most likely smarter than the leader (that’s why you hired them!), to do what they do best.

If you want to achieve the best, get out of their way and trust!

That said, how do we prevent the organization from falling into chaos where one team is using Clojure, another Haskel, another Go, another Agile, another Waterfall, ad-infinitum?  If we allow that sort of chaos to ensue, the engineering team will have serious issues with business continuity (shoot, Bob the COBOL guy just left – who here knows COBOL, anyone, anyone, Bueller?), hiring, and maintainability.  The other extreme, which large organizations often adopt, is a regimented approval process for all technology, down to the version level.  Do you want to use Scala 2.11.7?  Well, write up a report, submit to the engineering committee, POC, review, and 6 months later and dozens hours of your time, maybe get Scala on the restricted list.  Maybe.

But there is a better way to do it and that is Managed Chaos.*  Chaos can be detrimental, but if properly managed, it can be a powerful tool for an engineering organization to embrace innovation and drive the business forward through technology.  When the software engineering team comes to you asking to use COBOL, say, “Yes! I support you.  But, I do have some questions.  Where do you plan on finding COBOL engineers?  What about mainframes?  I don’t have any here.  Also, can you build websites in COBOL?  Didn’t know that.  How will you leverage other teams to get the help you need since no one here knows COBOL?”  If the team has justification and has reasoned out how this will drive the product and team, allow the chaos to happen.  You have managed the chaos by asking the right questions, thinking things through, and aligning to the business and product. You have embraced change that can drive the business and product forward by giving your team control (e.g. the tech stack).

Create a great engineering environment by giving your team control.

What you really are trying to avoid is the “Oh, yeah, we should have thought of that” statement after the fact.  For example, if our Data Science team wants to start using MongoDB and our Platform team wants to use Cassandra, that is totally fine if they have thought it through and discussed why each team needs to use a different no-sql databases.  That is Managed Chaos.  We don’t want to have the discussion 6-months after implementation and say, “I guess there is no reason we couldn’t have used the same.”  That is just chaos.

Another example is from a recent decision by one of our Agile teams at TheLadders.  Most teams are either singularly use either Jira or Trello.  However, one team for our Professional website decided they want the Product Owner to create user stories in Jira and the Engineering Tasks be broken down and estimated in Trello.  I was dubious – really, both?  Isn’t that, well, chaotic?  It isn’t.  The team had thought though the complexity and determined it worked for both the product owner and engineers, and I’m happy to say they were right.

In conclusion, don’t be afraid of change, don’t be afraid of the decisions your team makes, and don’t be afraid of managed chaos.

 

Managed Chaos is similar in concept to Fractal Organizations.  Below is a great description from Pravir Malik on how a Fractal Organization works:

“In meta-theory all instances of organization, from the smallest, such as an idea or a person, to the largest, such as global markets and planet earth herself, are fractals:  the essence of their way of being is repeated on scales both smaller and larger than themselves.  There is however, a particular class of fractals, that of progress, of which The Fractal Ladder is an ever-present manifestation, which spawns organizations that are truly progressive and sustainable in nature.  In the scheme of things this class of fractals is of critical importance, and to master its replication and to fully understand the impact it will have in creating sustainable and dynamic organizations is a practical necessity.”

Visual State Testing with Mock Data

Testing is organized skepticism.

–James Bach

The Problem

Here at TheLadders we’ve been working on building new pages designed to provide job seekers access to data we’ve been able to collect over the years in new and interesting ways. These pages are static in design, but the amount of data available for each page can vary widely. This means we have a wide range of visual states for every element on these pages. Compounding the problem are occasional elements which impact the layout of their neighbors depending on their own state. All this brought us to a very clear need to test these visual states in an automated way.

Requirements

We wanted to cover all of our visual states. This meant we needed to make creating state combinations easy. Covering all of our states lets us add new elements to existing pages without worrying about breaking existing layouts. It also lets us modify shared styles and scripts without worrying about breaking layouts already using them.

We wanted to make sure these tests were automated and reproducible. They needed to integrate into our development, QA and release processes. Developers shouldn’t have to worry about remembering to run tests. We’ve written about how we accomplish this before with our friendly testing framework.

We wanted to minimize dependencies on services or data outside of our code. Tests shouldn’t force developers to jump through additional hoops in order for the tests to run. We can’t let internal network or DB issues stop development. And we can’t let code and data get out of sync for automated tests.


 

Our Solution

URL Parameters

To meet all of these requirements we use a set of optional URL parameters to force our server to generate mock data to exercise all of the UI elements on a given page. This lets us quickly define full page tests with different combinations of parameters to generate any and all possible states. These parameters are only respected in local and QA environments to keep our production site clean.

Example URLs:

For our companies pages, we expose the following parameters:

1
2
3
4
mockData : should the server return real or mock data?
detailLevel : controls the mock basic details data (description, location, details, etc)
salaryPoints : controls how many mock salary points to return
similarCompanies : controls how many mock similar company entries to return

Parameters generally limit their options to meaningful data densities. E.G. “full”, “minimal”, “empty”. This decouples the test code from the page layout details.

To force a layout where we have only limited data for a page, the company URL would look something like:

1
https://qa-1/companies/company-name/?mockData=true&detailLevel=sparse&salaryPoints=none&similarCompanies=minimal

And results in a page which looks like:

And to force a layout with lots of details and available data:

1
https://qa-1/companies/company-name/?mockData=true&detailLevel=verbose&salaryPoints=full&similarCompanies=extra

Resulting in a page like:

In order to test as much of the stack as we can and increase our test coverage for free, these parameters are passed all the way to the point in the code where we query our data stores, at which point the code simply has to check the flags to determine which data store, real or mock, to retrieve data from. This also means that the only code which has to change is the data retrieval code. No other server code and no client code has to worry about where the data is coming from. As long as it’s in the same format, they handle it just like real data from our data stores.

The Mock Data

Once we defined how we were going to request mock data, we had to decide how to provide it. The brute force approach of forcing developers to hand-code all desired variations wasn’t acceptable. This would have been a burden on developers and the end result would have been brittle. Any future updates to the data models would have forced developers to go back and update all of the previously defined mock data.

Our approach is to randomly generate all mock data. This way developers only have to define a generation function once per data point and then simply generate as many instances of those data points as are requested. This makes it much easier to cover all data states and eliminates hand coded data as long as you provide re-usable utility methods for string fields like names and titles.

The Code

Our server code is written entirely in Scala with Spring bindings.

Example entry point code:

Example entry point code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  @RequestMapping(Array("/companies/{companyName}"))
  def companyPage(@PathVariable companyName: String,
                  @RequestParam(defaultValue = "false") useMockData: Boolean,
                  @RequestParam(defaultValue = "full") mockDetailLevel: String,
                  @RequestParam(defaultValue = "full") mockSalaryPoints: String,
                  @RequestParam(defaultValue = "full") mockSimilarCompanies: String ): Any = {

    val request = CompanyRequest(companyName,
                                 useMockData && Environment.current != Prod,
                                 mockDetailLevel,
                                 mockSalaryPoints,
                                 mockSimilarCompanies)

    val result = doCompanyQuery(request)

    buildView(result)
  }

Example data query code:

Example data query code
1
2
3
4
5
6
7
8
9
10
11
  def querySimilarCompanies(request: CompanyRequest): Seq[SimilarCompany] = {
      if (request.useMockData) MockData.similarCompanies(request.mockSimilarCompanies)
      else realSearchGateway.similarCompanies(request.companyName)
  }

  def similarCompanies(switch: String): Seq[SimilarCompany] = match switch {
    case "minimal"  => buildSimilarCompanies(5)
    case "full"     => buildSimilarCompanies(24)
    case "extra"    => buildSimilarCompanies(100)
    case _ | "none" => Seq.empty
  }

Example mock data generation code:

Example mock data generation code
1
2
3
4
5
6
7
8
9
10
  private final val BUILD_SIMILAR_SEED = 9284756

  def buildSimilarCompanies(count: Int): Seq[SimilarCompany] = {
    val rng = new Random(BUILD_SIMILAR_SEED)
    for (i <- 1 to count) yield {
      SimilarCompany(name = getRandomCompanyName(rng),
                     similarity = rng.nextFloat,
                     openJobs = rng.nextInt(50))
    }
  }


 

Gotchas

There are a few things to look out for with this approach. The first is to make sure the mock data generated is the same every time you run the test. This is as simple as seeding your random number generators and making sure to create them fresh for every request batch. If you don’t seed your random number generator you’ll get different results ever time, and if you don’t create a new seeded generator for each request batch the order of your tests will change what data is actually generated.

With all of this great UI test coverage it’s important not to neglect the code which actually queries the real data stores. Now that most tests never have to hit the data stores, it’s especially important to cover the data query code with their own unit and integration tests.

The flags and settings for mock data are specified at the entry points, but they’re not used until much further down the call chain. This can lead to mock-related code sprinkled throughout the code path if you’re not careful. In our current implementation we capture the mock flags in our entry points and store them in a single object which is passed to all data retrieval methods. This has been a nice simple approach for us which contains the flags to a single object. Each data-store gateway does still need to know how to provide mock or real data based on those flags, but keeping the mock data generation code centralized to one object per-page helps re-centralize the problem.

And finally we’ve found it’s best to have as many query switches as you have stateful elements on your page. This makes it easy to compose and maintain any number of tests from the individual mock data pieces. Most of our mock data generation is extended by our technical QA to help cover all of the UI states. It also lets us easily test components in isolation where necessary.

 


 

Conclusion

This approach to testing our visual states with mock data has proven invaluable as we build out more and more information-centric pages. I hope this approach finds a useful place in your arsenal of testing strategies on your current or future projects.