I'm really interested in the trends we see in the software engineering job market. Sometimes it's really hard to tell a cohesive and accurate narrative about what's happening because it just happens so dang fast, and very few people are collecting data on the matter. For example, here are some questions that I'd like to know the answer to: Does "DevOps" and increased cloud usage mean fewer traditional ops jobs? Do things like Firebase and Supabase mean fewer back-end jobs? Do things like tailwind mean fewer designers? Are we moving into more or more roles? specialist generalist Are people still specializing as DBAs? I aggregated all the answers about developer employment from the last 9 years of Stack Overflow surveys in order to start being able to answer some of these questions. If you're curious about the , hopefully, this is helpful data. job outlook for developers First, a Note on the Data All the numbers in the tables represent the percentage of survey takers who identified themselves as a given job type. The questions asked by SO have changed over the years, so we should take that with a giant grain of salt. I've normalized the answers. For example, "developer, back-end" and "back-end web developer" I've grouped together as "backend". Stack overflow allowed people to select 0-many job titles after 2015, before that, it appears they were limited to one. In some years developers had more job options to pick from. I've thrown away a lot of off-topic job types like "enterprise services developer" and "elected official". This is only Stack Overflow data, so it's biased by the size of their user base each year. That said, let's get into the data. I'll drop the script I wrote to create the aggregate data as well as a link to the raw data at the bottom of this article. Web Trends: Full-Stack, Front-end, and Back-end year full-stack frontend backend 2013 24.5 4.31 7.88 2014 25.72 5.02 9.3 2015 25.93 4.76 8.07 2016 37.88 5.13 10.82 2017 51.05 2.47 5.08 2018 44.87 35.22 53.92 2019 47.5 29.98 45.75 2020 42.08 28.38 42.24 2021 39.42 21.85 34.84 2022 39.17 21.72 36.99 In 2013, the ratio of "front-end" developers to "full-stack" developers was 15/85, while the ratio of "back-end" developers to "full-stack" developers was 24/76. The trend of "more full-stack, less specialization" continues through the entire ten years of data. It's important to note that in 2017/2018, the survey changed drastically, which is why we see a big change in the numbers. Interpretation #1 It seems reasonable to conclude that the trend for the last decade has been that there is a shrinking percentage of developers who do front-end work or back-end work. More and more are doubling as full-stack engineers. only only Keep in mind, this also be that there have been more small to mid-sized companies over the years. Smaller companies typically require more generalists, but now I'm just guessing. might Interpretation #2 The ratio of front-end/back-end engineers has stayed fairly consistent, with there being slightly less than twice as many back-end engineers than front-end engineers. This actually surprised me, I expected the ratio of front-end to back-end engineers to be closer throughout the years. IT Operations Trends: Back-End, DevOps, and Traditional Ops year backend DevOps ops 2013 7.88 0 2.96 2014 9.3 1.61 2.85 2015 8.07 1.23 1.77 2016 10.82 1.92 1.79 2017 5.08 7.81 13.66 2018 53.92 9.66 18.33 2019 45.75 11.43 15.93 2020 42.24 10.51 13.11 2021 34.84 9.75 10.82 2022 36.99 13.2 11.8 In my script, I tried to split more "traditional ops" roles into the "ops" category, and the "DevOps" stuff into the "DevOps" role. For example, "SRE" I've considered as "DevOps", while "systems administrator" is "ops". Caveat on the Data for 2017 What the hell happened in 2017? The data honestly just seems broken. I dug through the data manually because according , they claim 24% of web developers said they were back end, and 75% of respondents claimed to be web developers. to the website At the moment, it's looking to me like they must have had some qualifiers on those numbers because it's just not adding up on my end. I'm going to exclude 2017 from my analysis in the interpretations. Interpretation 1 DevOps appears to be gaining ground on traditional ops. In 2013, no one was identifying themselves as a "DevOps" person, but by 2020 and 2021, the numbers are looking similar. very It's worth pointing out that in 2016, the DevOps numbers actually eclipsed the "ops" numbers for a single year. My best guess is that 2016 was about when many companies started simply rebranding their "ops" teams as "DevOps" teams in order to look cool. It's hard to care too much about these numbers because in my opinion. I don't trust that the "ops" titles and the "DevOps" titles are all that different at most companies. DevOps is mostly being done wrong Interpretation 2 "DevOps" seems to be trending down the least in recent years, in fact, it had a nice jump in 2022. However, if you look at "DevOps" and "ops" together, then the category still trending down a bit. is Interestingly "ops" has been trending down from the start, while "back-end" was trending up until 2016 when the trend reversed and it's been down since. At first, I assumed we were simply seeing the same trend that we saw in web development: more generalists, less specialists. However, I grew skeptical because when I looked across job categories, I noticed nearly of them were trending down... which clearly can not be the case when we're looking at percentages - it's a zero-sum game. all all I decided to add a new section to my script to dig in further. I calculated how many jobs on average each survey taker was laying claim to, and got this data: year backend DevOps ops avg_jobs_per_user 2013 7.88 0 2.96 1 2014 9.3 1.61 2.85 1 2015 8.07 1.23 1.77 1 2016 10.82 1.92 1.79 1.89 2017 5.08 7.81 13.66 2.48 2018 53.92 9.66 18.33 2.79 2019 45.75 11.43 15.93 2.84 2020 42.24 10.51 13.11 2.59 2021 34.84 9.75 10.82 2.21 2022 36.99 13.2 11.8 2.27 It appears that from 2013-2015, developers were restricted to only submitting a single answer, which helps to account for the super low numbers. However, from 2019->2021 the average number of jobs per user went , which is antithetical to the "more generalists" theory. down It's also worth pointing out, that as the years went on, Stack Overflow actually added more specialized categories, which I then took the liberty of grouping into these broader groups. So there's actually good evidence that developers specializing more, or at the very least that there are more in which one can specialize. are possible ways That said, even after looking at this data I think there is still a good case to be made that , especially at smaller companies. back-end developers will be doing more and more "DevOps" work Data Trends: Data Science, Data Engineering, and Back-End year type_data_science data_engineer backend 2013 0 0 7.88 2014 0 0 9.3 2015 2.12 0.69 8.07 2016 3.83 0.7 10.82 2017 9.14 0 5.08 2018 7.17 0 53.92 2019 7.27 6.55 45.75 2020 6.19 5.8 42.24 2021 5.12 5 34.84 2022 4.67 4.91 36.99 Interpretation It's super interesting to me that data engineering really only started to appear in the survey data in 2019. Until then, I'm guessing back-end engineers and data scientists swallowed up that role. That new specialization is certainly interesting. Machine learning has absolutely grown over the last decade, but it looks like there may have been a bit of a "hype bubble" in 2017? The Rest of the Data I've talked about my personal interpretations regarding the data that I found the most interesting, but here's the data I aggregated so you can inspect it yourself: all year avg_jobs_per_user full-stack frontend backend DevOps ops mobile desktop embedded data_science data_engineer game management QA education design analyst marketer ignore 2013 1 24.5 4.31 7.88 0 2.96 6.48 9.53 2.16 0 0 0 7.49 0 0 0 0 0 34.7 2014 1 25.72 5.02 9.3 1.61 2.85 7.57 9.43 2.42 0 0 0 5.9 0 0 0 0 0 30.18 2015 1 25.93 4.76 8.07 1.23 1.77 7.28 6.65 2.33 2.12 0.69 0.63 1.44 0.63 0 0.57 0 0.23 35.66 2016 1.89 37.88 5.13 10.82 1.92 1.79 7.39 6.05 2.26 3.83 0.7 0.52 10.04 0.68 0 0.59 1.02 0.21 98.65 2017 2.48 51.05 2.47 5.08 7.81 13.66 16.2 20.3 6.52 9.14 0 3.37 0.5 2.44 1.42 3.94 3.69 0.3 100 2018 2.79 44.87 35.22 53.92 9.66 18.33 19.02 15.99 4.87 7.17 0 4.7 7.99 6.27 3.68 12.16 7.65 1.13 26.63 2019 2.84 47.5 29.98 45.75 11.43 15.93 16.54 19.48 8.15 7.27 6.55 4.99 6.34 7.15 10.18 10.33 7.08 1.1 28.37 2020 2.59 42.08 28.38 42.24 10.51 13.11 14.71 18.28 7.37 6.19 5.8 4.33 5.54 6.12 8.67 8.25 6.24 1 30.18 2021 2.21 39.42 21.85 34.84 9.75 10.82 11.74 13.23 5.51 5.12 5 2.53 6.37 4.33 5.56 5.53 4.54 0.76 34.51 2022 2.27 39.17 21.72 36.99 13.2 11.8 10.42 13.03 5.35 4.67 4.91 2.51 10.44 4.23 5.78 5.14 4.37 0.71 32.06 Raw CSV Data Here's a link to the on Stack Overflow. raw CSV data My Script Here's the full Python script I used to crunch the numbers. Sorry for the code sloppiness, I didn't sink a ton of time into the code. The most interesting part of the script is probably the function near the bottom, that's where I aggregate all of the many job types reported by stack overflow users into the few I included in the chart. get_mapped_job import csv outpath = "csv/out.csv" type_devops = "devops" type_ops = "ops" type_backend = "backend" type_frontend = "frontend" type_mobile = "mobile" type_fullstack = "fullstack" type_desktop = "desktop" type_embedded = "embedded" type_data_science = "data_science" type_ignore = "ignore" type_management = "management" type_education = "education" type_design = "design" type_marketer = "marketer" type_data_engineer = "data_engineer" type_game = "game" type_analyst = "analyst" type_qa = "qa" def main(): files = [ (2013, [6]), (2014, [6]), (2015, [5]), (2016, [8, 9, 10]), (2017, [14, 15, 16, 17]), (2018, [9]), (2019, [12]), (2020, [13]), (2021, [11]), (2022, [11]), ] out_dict = {} jobs_per_user_dict = {} for f_tup in files: counts = {} path = f"csv/{f_tup[0]}.csv" print(f"generating report for {path}") out_dict[f_tup[0]]: {} with open(path, "r") as csvfile: rows = csv.reader(csvfile, delimiter=",") count = 0 rows_cpy = [] jobs_per_user = [] for row in rows: count += 1 rows_cpy.append(row) for row in rows_cpy: try: jobs = get_jobtext_from_cells(f_tup[1], row) mapped_jobs = set() for job in jobs: mapped_jobs.add(get_mapped_job(job)) jobs_per_user.append(mapped_jobs) for mapped_job in mapped_jobs: if mapped_job not in counts: counts[mapped_job] = 0 counts[mapped_job] += 1 except Exception as e: print(e) avg_jobs_per_user = 0 for user_jobs in jobs_per_user: avg_jobs_per_user += len(user_jobs) jobs_per_user_dict[f_tup[0]] = round( avg_jobs_per_user / len(jobs_per_user), 2 ) for job in counts: counts[job] /= count counts[job] *= 100 counts[job] = round(counts[job], 2) out_dict[f_tup[0]] = counts write_out(out_dict, jobs_per_user_dict) def get_jobtext_from_cells(indexes, row): if len(indexes) == 0: return [] job_texts = [] for i in indexes: cell = row[i] cell_job_texts = cell.split(";") job_texts += cell_job_texts return job_texts def write_out(out_dict, jobs_per_user_dict): types = [ type_fullstack, type_frontend, type_backend, type_devops, type_ops, type_mobile, type_desktop, type_embedded, type_data_science, type_data_engineer, type_game, type_management, type_qa, type_education, type_design, type_analyst, type_marketer, type_ignore, ] with open(outpath, "w") as csvfile: w = csv.writer(csvfile) w.writerow(["year", "avg_jobs_per_user"] + types) for year in out_dict: row = [year, jobs_per_user_dict[year]] for t in types: row.append(out_dict[year][t] if t in out_dict[year] else 0) w.writerow(row) def get_mapped_job(job): job = job.lower().strip() if job == "": return type_ignore if job == "devops specialist": return type_devops if job == "designer": return type_design if job == "c-suite executive": return type_management if job == "analyst or consultant": return type_analyst if job == "back-end developer": return type_backend if job == "windows phone": return type_mobile if job == "i don't work in tech": return type_ignore if job == "growth hacker": return type_marketer if job == "desktop developer": return type_desktop if job == "analyst": return type_analyst if job == "executive (vp of eng., cto, cio, etc.)": return type_management if job == "mobiledevelopertype": return type_mobile if job == "engineer, data": return type_data_engineer if job == "graphics programmer": return type_game if job == "systems administrator": return type_ops if job == "developer, game or graphics": return type_game if job == "desktop software developer": return type_desktop if job == "nondevelopertype": return type_ignore if job == "elected official": return type_ignore if job == "engineering manager": return type_management if job == "web developer": return type_fullstack if job == "machine learning specialist": return type_data_science if job == "data or business analyst": return type_analyst if job == "devtype": return type_fullstack if job == "response": return type_ignore if job == "developer, qa or test": return type_qa if job == "machine learning developer": return type_data_science if job == "developer, front-end": return type_frontend if job == "database administrator": return type_ops if job == "android": return type_mobile if job == "webdevelopertype": return type_fullstack if job == "blackberry": return type_mobile if job == "system administrator": return type_ops if job == "mobile developer - android": return type_mobile if job == "developertype": return type_fullstack if job == "ios": return type_mobile if job == "developer with a statistics or mathematics background": return type_ignore if job == "qa or test developer": return type_qa if job == "educator or academic researcher": return type_education if job == "engineer, site reliability": return type_devops if job == "marketing or sales professional": return type_marketer if job == "student": return type_ignore if job == "back-end web developer": return type_backend if job == "educator": return type_education if job == "front-end developer": return type_frontend if job == "developer, desktop or enterprise applications": return type_desktop if job == "senior executive/vp": return type_management if job == "occupation": return type_ignore if job == "scientist": return type_ignore if job == "developer, full-stack": return type_fullstack if job == "graphic designer": return type_design if job == "developer, embedded applications or devices": return type_embedded if job == "embedded application developer": return type_embedded if job == "quality assurance": return type_qa if job == "graphics programming": return type_game if job == "senior executive (c-suite, vp, etc.)": return type_management if job == "it staff / system administrator": return type_ops if job == "business intelligence or data warehousing expert": return type_data_engineer if job == "full stack web developer": return type_fullstack if job == "developer, mobile": return type_mobile if job == "front-end web developer": return type_frontend if job == "desktop applications developer": return type_desktop if job == "other (please specify):": return type_ignore if job == "mobile developer": return type_mobile if job == "devops": return type_devops if job == "enterprise level services developer": return type_ignore if job == "data scientist": return type_data_science if job == "executive (vp of eng, cto, cio, etc.)": return type_management if job == "mobile developer - ios": return type_mobile if job == "game or graphics developer": return type_game if job == "which of the following best describes your occupation?": return type_ignore if job == "other": return type_ignore if job == "desktop or enterprise applications developer": return type_desktop if job == "c-suite executive (ceo, cto, etc.)": return type_management if job == "embedded applications/devices developer": return type_embedded if job == "product manager": return type_ignore if job == "mobile application developer": return type_mobile if job == "mobile developer - windows phone": return type_mobile if job == "data scientist or machine learning specialist": return type_data_science if job == "educator or academic": return type_education if job == "embedded applications or devices developer": return type_embedded if job == "quality assurance engineer": return type_qa if job == "enterprise level services": return type_ignore if job == "full-stack developer": return type_fullstack if job == "na": return type_ignore if job == "academic researcher": return type_education if job == "manager of developers or team leader": return type_management if job == "marketing or sales manager": return type_marketer if job == "developer, back-end": return type_backend if job == "full-stack web developer": return type_fullstack if job == "designer or illustrator": return type_design if job == "programmer": return type_ignore if job == "developer": return type_ignore if job == "manager": return type_management if job == "engineer": return type_ignore if job == "sr. developer": return type_ignore if job == "full stack overflow developer": return type_fullstack if job == "ninja": return type_ignore if job == "mobile dev (android, ios, wp & multi-platform)": return type_mobile if job == "expert": return type_ignore if job == "rockstar": return type_ignore if job == "hacker": return type_ignore if job == "guru": return type_ignore if job == "self_identification": return type_ignore if job == "occupation_group": return type_ignore if job == "cloud infrastructure engineer": return type_devops if job == "project manager": return type_management if job == "security professional": return type_ops if job == "blockchain": return type_backend if ( job == "mathematics developers (data scientists, machine learning devs & devs with stats & math backgrounds)" ): return type_data_science raise Exception(f"job not mapped: {job}") main() Also published here