<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jess Solka</title>
    <description>The latest articles on DEV Community by Jess Solka (@jesssolka).</description>
    <link>https://dev.to/jesssolka</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F340054%2Fce00ade8-9fa7-41a8-947d-254909a743c0.jpeg</url>
      <title>DEV Community: Jess Solka</title>
      <link>https://dev.to/jesssolka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jesssolka"/>
    <language>en</language>
    <item>
      <title>Reflections on my career as a woman in tech</title>
      <dc:creator>Jess Solka</dc:creator>
      <pubDate>Mon, 04 Jan 2021 20:48:08 +0000</pubDate>
      <link>https://dev.to/jesssolka/reflections-on-my-career-as-a-woman-in-tech-3l9e</link>
      <guid>https://dev.to/jesssolka/reflections-on-my-career-as-a-woman-in-tech-3l9e</guid>
      <description>&lt;p&gt;I was inspired to share my story because I'm coming up on 7 years in tech, and the new year was causing me to remember and reflect. I hope it can be helpful for others who have had similar experiences to know you are not alone, and to raise more awareness of what it can be like to be a woman in the tech industry.&lt;/p&gt;

&lt;p&gt;This is a long story. Feel free to skip to the bottom for salary info.&lt;/p&gt;




&lt;p&gt;I entered my career in software engineering from a nontraditional background. I don't have a computer science degree. I didn't go to a bootcamp. I grew up building websites with CoffeeCup HTML editor, Expage, and LiveJournal (not Neopets, though). I took three years of programming in high school, but that is the extent of my formal computer science education. Approximately 7 years ago, I transitioned from teaching children with special needs into tech.&lt;/p&gt;

&lt;h3&gt;
  
  
  2014
&lt;/h3&gt;

&lt;p&gt;I'm working with kids, traveling to their houses to provide therapy. I'm burning out from working from 8am-6pm but only getting paid for 5 hours of it due to travel time, nap time, etc... Because of insurance rules, despite having time off banked, I have to work weekends to make up for any hours I'm out sick or on vacation. I get yelled at by rich white housewives for ending a session early because their child has a fever and green snot. I needed a break.&lt;/p&gt;

&lt;p&gt;I didn't know what I wanted to do but I had a degree in psychology, knew I could write/research, and lived in DC – so maybe policy work? I had also read a reddit thread about "cool careers you never knew existed" that mentioned this thing called &lt;em&gt;QA&lt;/em&gt; that seemed pretty spiffy.&lt;/p&gt;

&lt;p&gt;So I put my resume up in about 1000 different directions. I made a "policy" resume that emphasized my research experience. I think I applied to some special education schools. And I made a "tech" resume that emphasized my problem-solving abilities.&lt;/p&gt;

&lt;p&gt;Long story short, I get a call about a Junior QA position and manage to convince them to hire me. It was the only interview I went on. I still don't know how that happened but I'm eternally grateful for it. So I go work for this multi-national company with a tiny satellite office in an office park in a suburb of Annapolis, driving ~50 minutes each way (reverse commute, though). My boss was a Black woman. I had no idea at the time how unusual that was. She was also the last time I had a manager that was not a white or Indian man.&lt;/p&gt;

&lt;p&gt;I don't remember too much about working there but I do remember being bored, and playing a lot of boardgames with the other QA engineer they eventually hired. Big companies move slow. I don't regret working there, though. How could I? They took me in and made me a QA Engineer from nothing. I learned about writing test plans. I learned about automation using Selenium. I revived the smoldering spark of Java knowledge that had been buried deep inside of me since high school. I didn't keep in touch with anyone but I do wonder what they're up to from time to time.&lt;/p&gt;

&lt;h3&gt;
  
  
  2015
&lt;/h3&gt;

&lt;p&gt;I worked there for a little over a year when I got a message from a recruiter. I don't remember if I was actively looking for a new job at the time. The message described a QA automation position and it seemed interesting.&lt;/p&gt;

&lt;p&gt;The job wound up being for some hardcore Republican data company. That was one of the worst interviews I've been through, to date. Bunch of old white men in a room grilling me on who my "favorite economist" was, because that is vital to automation engineering. I distinctly remember them asking me who my favorite &lt;em&gt;recent&lt;/em&gt; president was, and in my head, I was like "don't say Obama don't say Obama don't say Obama," because I wanted to make it out of there alive. I stumbled enough that they expanded it to any president ever. "Uhh... Abraham Lincoln," I said. The voice in my head said, "oh shit no he freed the slaves - they probably hate him." I called up the recruiter immediately after I left and withdrew myself from consideration.&lt;/p&gt;

&lt;p&gt;I was ready to give up on that recruiter, but then he told me about a startup that was a 10 minute walk from my apartment. That was compelling, given that the &lt;del&gt;road rage&lt;/del&gt; commute was taking years off my life. I got an offer as a Junior QA Engineer at almost twice my salary at the time, accepted it, and became the first woman on their engineering team of around 20 people.&lt;/p&gt;

&lt;p&gt;I had three managers at that startup. The first was the CTO, an Indian man. I managed to score a promotion from Junior QA Engineer to QA Engineer when he was fired. That was also the first (but definitely not the last) manager who had zero insight into the scope of what I did on a daily basis. I remember that, in my official performance review with him, his feedback was that I start doing a task that I'd had complete ownership over for months. In my performance review, he attributed the work I was doing to the other QA engineer, a white man.&lt;/p&gt;

&lt;p&gt;After that, I had a white man as a manager for a couple of months, before he was laid off. Then another white man. After the layoffs, almost all of the rest of the engineers quit. I was picking up more and more development work because that was where I wanted my career to go, and there weren't many engineers left to do it. I had been teaching myself development in my spare time, making side projects, some of which I, and others, used at work.&lt;/p&gt;

&lt;p&gt;In my second (and final) annual performance review, I was instructed to "focus on QA." It's not fair to &lt;em&gt;the other QA engineer&lt;/em&gt;, my manager said. I wasn't allowed to do development work because we had to hire another QA engineer first. But we weren't hiring for another QA engineer - we were hiring devs. During my tenure at that company, two QA engineers - both white men - moved into development.&lt;/p&gt;

&lt;p&gt;I went through a lot of shit at that job. At the time, I attributed it to anti-QA culture. But in retrospect, that was naive. The story will seem pretty familiar to some of you all:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;being left out of important meetings&lt;/li&gt;
&lt;li&gt;literally being ignored when I answered a question posed to the room&lt;/li&gt;
&lt;li&gt;constant tone-policing from managers and peers despite there being a (white male) developer who literally banged the table  and another (white male) developer who was generous with the middle-finger emoji in Slack, both of whom had zero consequences&lt;/li&gt;
&lt;li&gt;being repeatedly publicly ridiculed for protesting being forced to work in the dark (some of the developers didn't like the glare on their monitors when the lights were on 🙄) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the layoffs, I repeatedly emphasized that I didn't feel like I was part of the team. The last thing my manager said to me at my goodbye happy hour has been stuck in my head since then. When I said something like, "I bet you're glad I'm leaving," was "no, we were excited to have a woman on the team."&lt;/p&gt;

&lt;p&gt;All that said, I definitely don't regret working there. Strong bonds form out of adversity. Three currently-ongoing romantic relationships were borne out of that time. Including mine - my husband, a software engineer from that company, and I got married in 2019. Several of our cohort were invited to our wedding, and three were in our wedding party. It was there that I taught myself Ruby (Rails) and Javascript (jQuery, Angular, and React), and in doing so, paved my way to becoming a developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  2017
&lt;/h3&gt;

&lt;p&gt;I wanted to be a dev. It was clear, after my performance review, that I had no path forward to do that at the startup where I was working. I started interviewing again.&lt;/p&gt;

&lt;p&gt;I interviewed at a lot more companies than I had in previous rounds. I accepted a position at a news company. It wasn't ideal - 6 months contract to hire, hourly, no health insurance for the first month. The tech was ancient.&lt;/p&gt;

&lt;p&gt;Technically, I had one manager during my time there, a white man. I say "technically" because I didn't actually know who my boss was. I had to ask HR who to give notice to when I quit. My tech lead was a really amazing woman, who really should have been my manager, but it was a flat org.&lt;/p&gt;

&lt;p&gt;I was given way more responsibility than I realistically probably should have been at that job. It was my first dev job and I was leading the technical direction for the company's newest, most highly-visible client. But we did it! And had loads of fun, and formed lasting friendships and many fond memories.&lt;/p&gt;

&lt;p&gt;We united over a common enemy - the client. The most amusing of whom was an engineer we nicknamed "Fonts Guy," who believed that fonts were the leading cause of website performance degradation. It was also the most diverse team I'd ever worked on, and embodied the culture of support that I came to look for in companies from then on.&lt;/p&gt;

&lt;p&gt;After my six months was up, I was offered a full time, salaried position. The pay wasn't great. The benefits were mediocre. I tried to negotiate, but was unsuccessful.&lt;/p&gt;

&lt;p&gt;I left that job because I couldn't handle clients. We needed a manager who was a "shit umbrella" - who would protect us from the client and the higher-ups in our own org. But we had, effectively, no manager at all. The client's designer would log 'P1* emergency' tickets for spacing being 2 pixels off (not even exaggerating, this actually happened). And then their other designer would log a 'P1 emergency' ticket to change it back.&lt;/p&gt;

&lt;p&gt;*P1 is the highest priority for incidents/bugs&lt;/p&gt;

&lt;p&gt;The last straw for me was when the sleaze-bag CTO pulled my team into a room and yelled at us about how the client was "losing faith" because of all the "bugs" they were finding. The bugs being, in general, along the same vein as the aforementioned mixed messaging regarding spacing.&lt;/p&gt;

&lt;p&gt;I put in notice and they offered me the world - significant salary increase, retention bonus, being moved to another team where I would never have to talk to a client again. My only regret in my entire career was not taking that counter offer. I was counseled by several people: "never take the counter offer." But I should have.&lt;/p&gt;

&lt;h3&gt;
  
  
  2018
&lt;/h3&gt;

&lt;p&gt;I took the next job at a modest pay increase because the health insurance was fantastic, because I would be able to write React, and because I wanted a strong managerial presence.&lt;/p&gt;

&lt;p&gt;It was a remote role for a company that was not remote-first. My manager, a white man, had been working there in the office for a few years and then moved to DC. I think they hired me to keep him company. I wanted a job where I had more managerial involvement, and being in a closet-sized room in a co-working space with just my manager and I seemed like a legitimate way to achieve that. Spoiler: though he was, indeed, physically present, he still managed to be absent when it came to anything related to work.&lt;/p&gt;

&lt;p&gt;I was hired to take an existing rails-served frontend and convert it to a single page React application. Nobody could really give me a good answer for why, because it was working just fine the way it was.&lt;/p&gt;

&lt;p&gt;I was miserable from the get-go. My team was composed of me, my manager, and the only other person who was allowed to review my pull requests - we'll call him Tom (a white man). I have no idea what Tom did all day, but it wasn't review (or create) PRs. I was &lt;em&gt;so&lt;/em&gt; bored.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4ojpa119e73os7pipryt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4ojpa119e73os7pipryt.png" alt="Screenshot of the 13 pull requests I had open, waiting for Tom to review, with the oldest being over a month old."&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;I got into a rhythm. I would spend a day or two putting up a whole bunch of PRs and then watch Netflix and YouTube all day for weeks at a time while I waited for them to be reviewed. I worked on my own projects when I could motivate myself to do so. It was horrifically demoralizing. Not having anything to do at work seems great in theory, but in reality, it's awful. You wake up every morning with no purpose, but you're tied to the computer in case &lt;em&gt;someone&lt;/em&gt; decides to notice you exist.&lt;/p&gt;

&lt;p&gt;Our manager was, as you would expect, entirely unhelpful in resolving this situation. Eventually, he decided that he didn't want to be a manager anymore. For a brief period of time, Tom and I reported to my former manager's manager (another white man). Shortly thereafter, as you may have predicted, Tom became my manager.&lt;/p&gt;

&lt;p&gt;I made zero friends at that job. I didn't even announce that I was leaving, outside of putting in notice, and neither did they. I learned and accomplished nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  2019
&lt;/h3&gt;

&lt;p&gt;This is when I started limiting my job search to only companies that have been vouched for by women in my network. I was referred to a small startup that needed someone to build them a frontend. They were pretty tiny at the time. They had a bunch of technical cofounders, but only about five engineering hires. I would report to the Head of Engineering (a white man).&lt;/p&gt;

&lt;p&gt;I tried, unsuccessfully, to negotiate my salary there too, but they asked me what I was currently making, and I made the cardinal sin of accurately answering. The CEO (who used to work at Google, as anyone who used to work at Google will tell you, repeatedly) refused to budge on more than a 10% increase.&lt;/p&gt;

&lt;p&gt;Over the next few months, we grew the team significantly, and I started reporting to the only good manager I've ever had (an Indian man). He supported me, listened to me, validated my feelings, and put me in positions that would set me up for success. He encouraged me to read the book &lt;em&gt;The Manager's Path&lt;/em&gt; so that I knew what to expect of him and could hold him to those expectations. Every engineer should read that book. Despite the title, it's not just for managers.&lt;/p&gt;

&lt;p&gt;I remember a turning point, where there was a choice between two projects. One was the "safe" road, where I would continue to work on the part of the system where I was the subject matter expert. The other was scary - tech lead a "tiger team" for a new feature that was integral to the success of the company and involved every system, many of which I'd had zero exposure to in my time there.&lt;/p&gt;

&lt;p&gt;I thought for sure that my manager would give the latter project to one of the devs who had more experience, but he wanted me to take it. I was scared, and because I trusted him, because he'd previously &lt;em&gt;heard&lt;/em&gt; me, I told him so. And he told me that I could do it, and that he would be there to support me. And he was. The project went really well! I discovered that I enjoyed leadership.&lt;/p&gt;

&lt;p&gt;My goal at this job became to achieve the title of Senior Engineer. Despite, in my first quarter there, solo-delivering a project that was vital to the company's ability to scale, and being the tech lead of another very successful project, my salary was consistent with the current market value of a junior engineer in DC. I figured, if I could at least get a title bump, I could make more at my next position.&lt;/p&gt;

&lt;p&gt;I asked the Head of Engineering - what do I need to do to become a Senior Engineer? His response was, at first, "we are too small for titles." There were around 30 engineers at the time, many of whom (all men) had Senior titles (this could be seen in the HR software we used). Later, and for many months, his response turned into "we are working on a career ladder." I never did see that career ladder.&lt;/p&gt;

&lt;p&gt;At some point, my awesome manager wanted to take a step back from management. My team transitioned under another (white male) manager. We can call him Simon. My awesome manager kept me on as his only report, though, because I'd made it clear in previous conversations that I didn't want to switch managers again (and probably also because I was known to be a "problem child" by that time). This is why I say I had 2.5 managers at that company - the new one still managed my daily team activities. &lt;/p&gt;

&lt;p&gt;The only thing I remember about Simon is that he tone-policed me more than once, and he really loved "puzzle" technical interview challenges. The experience that sticks out in my mind was when a friend and colleague asked how a meeting went and I responded something like "awful" (because I'd been talked over, not listened to, and condescended by a room full of men). Simon (remember, not even my real boss) pulled me into a room and told me that I need to be positive. That "people like you because you're positive."&lt;/p&gt;

&lt;p&gt;I was coming up on my one year. During that time, HR and the Head of Engineering were also "working on" an annual review process. Because the woman who had referred me there had been there for several months before I joined and still hadn't had an annual review, I suspected that process was going to be more vaporware. The only policy they'd managed to come out with was regarding conferences and it was "no conferences."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9jq5zr1aeg5luoz9u0nd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9jq5zr1aeg5luoz9u0nd.png" alt="Screenshot of Conference Attendance Policy: 1. There must be a demonstrable business value to your role and the company to attend. 2. All associated costs (registration fees, travel, accommodations, meals, etc) are employee-paid and non-reimbursable to the company. 3. The employee should not be out of the office for more than 2 business days. 3. Manager approval is required before registering and you must designate someone as your backup to cover your work while you're away."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I decided that the only way I was going to get a title or salary bump was to have another offer in hand, so I started interviewing. In my first 1-1 after having been there a year, I asked, again (this was not the first time I'd brought it up, obviously), about annual reviews, and was told it was still a "work in progress." So I told my manager - I have another offer. This was the good manager, so he said he would get me a review no matter what. That he owed it to me. He did, and a salary bump, but by that time, I was so disillusioned with the company that I couldn't stay.&lt;/p&gt;

&lt;p&gt;There was other dysfunction I don't want to expand on too much, like the fact that the CEO expressly believed in a "pull" method of communication, rather than "push". Which meant that there was no official communication about anything (including about employee benefits). Anything we wanted to know about the company, it had to occur to us to ask during the monthly Q&amp;amp;A. The company was slowly devolving into "startup cult" status, and the last meeting I went to, the CEO gave a speech about how we all had to "thrive" together, and we (the employees) couldn't "thrive" unless the company "thrives," and we couldn't allow one employee to "thrive" more than another. I'm now triggered by the word "thrive."&lt;/p&gt;

&lt;p&gt;Before I formally put in notice but after I'd informed my manager of my other offer, the CEO wanted to meet with me, 1:1. What I learned from that: &lt;em&gt;never&lt;/em&gt; meet with a CEO 1:1. He spent an hour gaslighting me, telling me that this was how all startups are, that I had unrealistic expectations, that what I was experiencing and feeling was wrong.&lt;/p&gt;

&lt;p&gt;He started out by asking me to talk, and he just listened. I had prepared in advance a list of things I was upset about. And then he went through each one, point-by-point, and told me why I was wrong. Later on, he would accuse me of "just telling [him] why [he] was wrong." It was so traumatic that several of my friends who'd passed by and seen us through the glass conference room walls checked in on me afterwards, and I cried myself to sleep that night thinking about it.&lt;/p&gt;

&lt;p&gt;He had prepared for the conversation by printing this image:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr3b5ds4m09vpjy01sdw9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr3b5ds4m09vpjy01sdw9.jpg" alt="Small carrot with big leaves and big carrot with small leaves"&gt;&lt;/a&gt;&lt;br&gt;
So that he could better illustrate how I was wrong about everything.&lt;/p&gt;

&lt;p&gt;I don't regret working at this company, because I grew a lot as an engineer there, in large part because of my one good manager who supported me. I also made a bunch of fantastic friends. I learned that I could create and foster the supportive culture that I loved at my newspaper job. For the first time, I considered that maybe I would want to be a manager some day.&lt;/p&gt;

&lt;p&gt;One of the companies I was interviewing with during that cycle was Facebook. It violated my "know someone who works there" rule, but I figured I probably wouldn't make it through the interview process anyway and wanted to give it a shot to see what it was like. They told me to spend weeks preparing by doing hackerrank challenges and the like, but I didn't, out of principle. Surprisingly enough, I made it to the final round.&lt;/p&gt;

&lt;h3&gt;
  
  
  2020
&lt;/h3&gt;

&lt;p&gt;I did not get an offer from Facebook. Which, I think, is for the best. Apparently, and to no one's surprise, I did great on both the manager and architecture sessions, but bombed the whiteboard coding sessions.&lt;/p&gt;

&lt;p&gt;I accepted the offer from the company at which I currently work. It was the first time I'd successfully negotiated - 90% because I was coached by other women about what to ask for, and 10% because they knew I was interviewing with Facebook. SHARE YOUR SALARIES, PEOPLE. The discomfort around talking about money is artificially manufactured by companies because it's in their best interest that employees remain isolated. I negotiated an increased salary and stock options, but was unable to negotiate for a higher title.&lt;/p&gt;

&lt;p&gt;I took the job, in large part because I had a couple of friends who worked there. Within a month of me starting, one friend quit and the other (the one who referred me) was laid off. C'est la vie.&lt;/p&gt;

&lt;p&gt;I still work here, so I probably shouldn't say too much, but suffice to say, I learned that the existence of a career ladder is &lt;em&gt;necessary but not sufficient&lt;/em&gt; for career progression at a company. I've had 3 managers since I joined in February, all white men, and I've been told I'll have a 4th (white, male) manager within the next couple of months.&lt;/p&gt;

&lt;h3&gt;
  
  
  2021
&lt;/h3&gt;

&lt;p&gt;It is now 2021! I am not sure what the future holds for me. I'm becoming increasingly convinced that starting my own company is the way to go. We'll see 🤷‍♀️&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;If my tech career was a book, English teachers would note a few common themes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switching jobs as the only means of career advancement&lt;/li&gt;
&lt;li&gt;Steady stream of mediocre, absent white male managers&lt;/li&gt;
&lt;li&gt;Making a bunch of amazing friends&lt;/li&gt;
&lt;li&gt;Teaching myself. A lot.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's also important to note that, though I've had a lot of shitty experiences with misogyny and sexism, I'm privileged in a lot of ways. Unlike some of my peers and friends, I haven't been sexually assaulted or harassed. I don't also have to contend with racism or transmisogyny. I know it's wise to avoid a game of oppression olympics, but it's also vital to point out that there are many, many stories of people who have dealt with way worse. So read those stories too, please.&lt;/p&gt;

&lt;h4&gt;
  
  
  The numbers
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Years: 7&lt;/li&gt;
&lt;li&gt;Jobs: 6&lt;/li&gt;
&lt;li&gt;Managers: 13.5 (1 woman, 12.5 men)&lt;/li&gt;
&lt;li&gt;Promotions: 1 (sort of)&lt;/li&gt;
&lt;li&gt;Titles: 4&lt;/li&gt;
&lt;li&gt;Salaries (to the extent that I can remember them):

&lt;ul&gt;
&lt;li&gt;~$45k&lt;/li&gt;
&lt;li&gt;~$80k&lt;/li&gt;
&lt;li&gt;~$90k&lt;/li&gt;
&lt;li&gt;$100k&lt;/li&gt;
&lt;li&gt;$110k&lt;/li&gt;
&lt;li&gt;$160k&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Check out what I'm working on
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://herewithme.chat" rel="noopener noreferrer"&gt;HereWithMe&lt;/a&gt; - Matches marginalized people in STEM in 1:1 voice chats for empathy and support&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tinythoughts.me" rel="noopener noreferrer"&gt;tinythoughts&lt;/a&gt; - A one sentence a day journal. Every day, write one line about your life.&lt;/p&gt;

</description>
      <category>womenintech</category>
      <category>inclusion</category>
      <category>career</category>
      <category>writing</category>
    </item>
    <item>
      <title>Caching firebase callable function requests with a service worker</title>
      <dc:creator>Jess Solka</dc:creator>
      <pubDate>Fri, 27 Nov 2020 14:34:54 +0000</pubDate>
      <link>https://dev.to/jesssolka/caching-firebase-callable-function-requests-with-a-service-worker-4jha</link>
      <guid>https://dev.to/jesssolka/caching-firebase-callable-function-requests-with-a-service-worker-4jha</guid>
      <description>&lt;p&gt;Recently, I had an idea for a quick project - &lt;a href="https://recipe.wtf"&gt;recipe.wtf&lt;/a&gt;. Given a url to a recipe, it scrapes the recipe text and ditches the ads, blogger's life story, etc... When I have an idea I want to spin up really quickly or prototype, I generally gravitate towards &lt;a href="http://firebase.com/"&gt;Firebase&lt;/a&gt;. It's super simple to set up, the documentation is pretty decent, and I find that it covers all of the bases for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Recipe.wtf consists of a static page (Firebase Hosting) and a single, idempotent function, an &lt;a href="https://firebase.google.com/docs/functions/callable"&gt;HTTPS Callable&lt;/a&gt; (similar to AWS Lambda). After releasing the MVP, I decided to make Recipe.wtf a PWA. This post is about one of the problems I had to solve: making the firebase callable function requests available offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Firebase callables make a POST request. &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-strategies"&gt;Workbox's caching strategies&lt;/a&gt; use the &lt;a href="https://w3c.github.io/ServiceWorker/#cache-put"&gt;ServiceWorker Cache API&lt;/a&gt;, which only supports GET requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Write a custom caching strategy using &lt;a href="https://developers.google.com/web/ilt/pwa/working-with-indexeddb"&gt;IndexedDB&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;My firebase callable function is called &lt;code&gt;getRecipe&lt;/code&gt;. It fetches the recipe url, parses the html, and returns the recipe text. The call to it from my React app is defined like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getRecipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;httpsCallable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getRecipe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  IndexedDB
&lt;/h3&gt;

&lt;p&gt;I decided to use the recipe url being fetched as the &lt;code&gt;key&lt;/code&gt;, and the stringified json response as the &lt;code&gt;value&lt;/code&gt; in my IndexedDB store. Because it was a simple key-value pairing, I opted to use the library &lt;code&gt;idb-keyval&lt;/code&gt;, which is a very simple, promise-based wrapper around the IndexedDB api. Highly recommend this library – it was super easy to set up and use.&lt;/p&gt;

&lt;p&gt;This wasn't super necessary, but I created a small wrapper around &lt;code&gt;idb-keyval&lt;/code&gt; with some error handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// indexdb.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;idb&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idb-keyval&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;savePostRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;idb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// something went wrong. Send it to Sentry/bugsnag/whatever&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getPostRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;idb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Key does not exist. No need to log error.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Service worker integration
&lt;/h3&gt;

&lt;p&gt;Now, we update the service worker file to use our cache api.&lt;/p&gt;

&lt;p&gt;First, we need to &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-routing#defining_a_route_for_non-get_requests"&gt;register a POST route with workbox&lt;/a&gt;. This tells our service worker to look for our route and intercept the requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;registerRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/getRecipe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;handlerCb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this was a GET request, instead of passing in our own &lt;code&gt;handlerCb&lt;/code&gt;, we would be able to use a workbox pre-defined caching strategy as the second param to the &lt;code&gt;registerRoute&lt;/code&gt; function, e.g. &lt;code&gt;new StaleWhileRevalidate()&lt;/code&gt;. But it isn't, so we will have to write our own caching strategy. Workbox has &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-routing#matching_and_handling_in_routes"&gt;decent documentation on that&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handlerCb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;RouteHandlerCallbackOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Requests can only be consumed once, so we need to&lt;/span&gt;
  &lt;span class="c1"&gt;// clone the request before we can get the payload. The&lt;/span&gt;
  &lt;span class="c1"&gt;// payload (in this case, the url) will be the cache key&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clonedRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;clonedRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Before making a request, check the cache. If our key&lt;/span&gt;
  &lt;span class="c1"&gt;// exists there, we don't need to make a request at all.&lt;/span&gt;
  &lt;span class="c1"&gt;// Ideally, there should be more logic here to call the&lt;/span&gt;
  &lt;span class="c1"&gt;// function anyway and check the diff, in case the&lt;/span&gt;
  &lt;span class="c1"&gt;// response returned from the function changes in the future&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;getPostRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Our request was not found in the cache – call the &lt;/span&gt;
  &lt;span class="c1"&gt;// function, cache it, and return it&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;savePostRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting it together, we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// service-worker.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RouteHandlerCallbackOptions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;workbox-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;registerRoute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;workbox-routing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getPostRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;savePostRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./indexdb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/** all the rest of the boilerplate service worker code **/&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handlerCb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;RouteHandlerCallbackOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clonedRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;clonedRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;getPostRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;savePostRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;registerRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/getRecipe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;handlerCb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Like I said, there is more work to do here, such as not caching empty responses, checking for diffs between the cache and new responses, etc... But I wasn't able to find a full working solution to this problem anywhere so hopefully this is helpful to someone!&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>firebase</category>
      <category>serverless</category>
    </item>
    <item>
      <title>What I do before I request a review on my pull request</title>
      <dc:creator>Jess Solka</dc:creator>
      <pubDate>Sat, 27 Jun 2020 20:58:22 +0000</pubDate>
      <link>https://dev.to/jesssolka/what-i-do-before-i-request-a-review-on-my-pull-request-29g9</link>
      <guid>https://dev.to/jesssolka/what-i-do-before-i-request-a-review-on-my-pull-request-29g9</guid>
      <description>&lt;p&gt;This is the first article in a series I'm writing to give practical advice to newer developers. I've found there are a lot of unwritten conventions or pieces of advice that we expect junior developers to "pick up along the way". Often, these are so entrenched in engineering culture, that they've turned into cliches, like "google your problem before you ask for help," that aren't elaborated on in a meaningful way. What types of problems can be solved by googling? What search terms do I use? How do I know what information is useful to solve my problem? As a senior engineer who has mentored engineers with less experience, I am going to attempt to elaborate on and document these as I encounter them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pull requests are one area with quite a few unspoken conventions
&lt;/h2&gt;

&lt;p&gt;There are a number of articles out there on the mechanics of making a pull request (hereafter referred to as PR). There are also a lot of articles about how to review a PR and how to respond to PR review feedback. I would like to talk about the in-between step. You've pushed up your branch, you've made a PR – is it ready for review? This is what I do when I've pushed up a PR, before I request a review from my team.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Check for merge conflicts
&lt;/h3&gt;

&lt;p&gt;Handily, Github tells you if your branch cannot be merged due to merge conflicts. The first thing I do after creating a PR is to check for and address these conflicts.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Check for failing continuous integration (CI) steps
&lt;/h3&gt;

&lt;p&gt;Depending on where you work, you may have CI (Github Actions, Travis, Circle, Jenkins, etc...) that runs against your PR. Commonly, this runs a linter and/or unit tests against your PR, and would block it from being merged if one of those fails. Before requesting a review from my coworkers, I address any failing steps and make sure all of the checks are green.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Read through the PR, as if I was reviewing it myself
&lt;/h3&gt;

&lt;p&gt;I check for things that need to be removed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is there any commented out code?&lt;/li&gt;
&lt;li&gt;Are there any stray logs or debugger statements?&lt;/li&gt;
&lt;li&gt;Are there any changes that are unrelated to my PR? These may be changes to code that should be part of a different branch, or accidental newlines added somewhere along the way. I like to remove even accidentally added whitespace because it leaves the commit history cleaner when others may be looking at the change history for a file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I check for things that need to be changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do I have any typos or misspellings in my variables or comments?&lt;/li&gt;
&lt;li&gt;Would my variable names make sense to a new person coming into the codebase with less context than I have?&lt;/li&gt;
&lt;li&gt;Do I have any code that looks copy-pasted that I could make more &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY&lt;/a&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I check for things that need to be added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the business logic in my code make sense from looking at it, or do I need to add some comments?&lt;/li&gt;
&lt;li&gt;Did I add a piece of code without adding a corresponding test for it?&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Some of the above are subjective, and knowing when you need to make changes will come with experience. You may still get comments or change requests around those things when others are doing a review for you. But some are easy to catch if you know to look for them, and fixing them beforehand is an important step towards taking ownership of your code, and a sign to your coworkers that you value their time and have done your due diligence.&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
