Software Development Is Not Beginner Friendly

As we are coming to the end of the first part of the Wallpaper Website tutorial series, I wanted to quickly reflect on a bit of feedback I’ve had regarding these videos.

The gist of this feedback was that I had miss-sold the course as being “beginner friendly”, when in fact, it has been quite a tricky course.

Here’s my view point, and as ever, feel free to disagree:

Software Development is hard.

There are two approaches I could take:

  1. Show you abstract examples where I cover concepts on an individual basis.
  2. Show you a complete, working example and all the things that getting to a “working” state entail.

My life would be easier if I opted for #1.

However, in my opinion, I would be misleading you if that’s all I showed you.

I’ve been writing software for over 15 years now, and I still regularly underestimate just how much effort is required to go from the idea phase through to “phew, it’s finally online” phase.

And as I’m sure you know, even once it’s online (or launched), it’s not done. Far from it. If anything, that’s where the real work actually begins.

Working in software development in 2017 is hard. There are a ton of things to cover. This series has been about the more traditional approach – using Symfony (or PHP) for everything, front end and back end.

In reality, and as we push further into the second and third parts of this series, to become the most employable and to stand out from being “just another dev” you will need to know more than just Symfony (or PHP). You almost inevitably will need to be decent at JavaScript, too.

Does this make the series harder? Yes, unfortunately.

Is this still stuff a beginner needs to know? In my opinion, yes.

There’s no point in just showing you what I consider the old school approach. At least, not unless all you ever want to do is build old school style websites.

My guess is that you want to make websites – or web-based applications – like those that you typically interact with in 2017.

Can PHP – and more specifically, Symfony – get you to this point?

Yes. But only half of the way there.

The other half needs to be done in JavaScript.

In other words, Symfony for your back end, and JavaScript for the front end.

Which brings me back to the original bit of feedback that this course is not beginner friendly.

I would argue that the industry is no longer beginner friendly.

There is an immense learning curve to simply getting started.

Fortunately, once you are over the initial hump, things do get a bit easier.

This isn’t a problem unique to software development. Many industries expect you to have a very broad and general level of knowledge before specialising in a particular area.

That’s my opinion on the subject. As ever, please do feel free to hit reply and let me know yours. I’m not saying I’m always right by any means, and I am not averse to having my view points challenged 🙂

Follow Up From Last Week

Last week I posed a couple of questions in my “Would you use Symfony for SaaS?” post:

  1. How many other developers are using Symfony for SaaS, or SaaS-like services?
  2. Is Stripe / payment integration something that many other devs want or need tutorial content on?

Firstly, thank you to everyone who replied. I really appreciate you taking the time to respond.

I have learned that the majority of you are not using Symfony to build your own SaaS, or SaaS-like service.

This is useful to me as it helps shape the content I aim to produce.

Even though most of you are not doing SaaS related stuff, the payment integration video idea does seem popular.

As such, I have added a payment integration course to the ToDo list. This will cover not just Stripe but also Braintree (aka PayPal), and all the fun that supporting multiple providers entails.

Video Update

This week saw three new videos added to the site.

https://codereviewvideos.com/course/let-s-build-a-wallpaper-website-in-symfony-3/video/adding-an-image-preview-on-edit

As mentioned earlier in this series, there is a way to integrate with VichUploaderBundle when working with EasyAdminBundle.

In doing so, one of the nice ‘bonus’ features is the image preview when viewing an existing entity with an attached image file.

In this video we look at a way to recreate this in our own implementation. It’s easier than you might think, and gives us a good opportunity to explore some of the less frequently used parts of Symfony’s form component.

https://codereviewvideos.com/course/let-s-build-a-wallpaper-website-in-symfony-3/video/delete-should-remove-the-wallpaper-image-file

One problem that’s been bothering me throughout this series is that when recording the videos I will do some uploading of images, and then delete the Wallpaper entity, but the image file I uploaded still remains on disk.

To fix this we can copy a process we’ve covered for both Create and Update – which is to say we can listen for a different Doctrine event, that of preRemove.

As we might be changing over to use a different storage mechanism in the longer term (think: Amazon S3, not storing everything on the server’s local hard disk), we also need to abstract away exactly how an image file is deleted.

We will no doubt revisit this (or a variation of this) during our test-driven approach.

https://codereviewvideos.com/course/let-s-build-a-wallpaper-website-in-symfony-3/video/getting-started-testing-wallpaper-updates

Speaking of tests, in this first video of the final part of this series, we are switching back to our test-driven approach and beginning the re-implementation of our Wallpaper Update and Delete features.

As ever, thank you very much for reading, have a great weekend, and happy coding.

Chris

How I Fixed: uncaught at check call: argument [object Promise] is not a function

Earlier this month I started dabbling with Redux Sagas as an alternative to Redux Thunks.

At the time I was highly skeptical – largely around whether re-writing a significant portion of my JavaScript was good use of my time, given that I had no prior experience with generators, and that conceptually sagas sounded pretty hard.

But I kept hitting issues with Thunks. They just seemed so cumbersome, and error prone. I found writing tests for them to be painful, and adding in the Redux API Middleware made that even more complicated.

Ultimately, switching to Redux Saga has been one of the highlights of my current project. I love it. Every problem I have had so far has been made easier through the use of sagas.

But today I hit on a problem I haven’t seen before:

uncaught at check call: argument [object Promise] is not a function

This is a generic version of the code I am using:

export function *doRequestProfile(action) {

  try {

    yield put({
      type: types.REQUEST__STARTED,
      payload: {
        requestFrom: 'my-saga'
      }
    });

    const profile = yield call(api.fetchProfile(action.payload.accountId));

    yield put({
       type: types.PROFILE__SUCCESSFULLY_RECEIVED,
       payload: {
         profile
       }
    });

  } catch (e) {

    yield put({
      type: types.PROFILE__FAILED_RECEIVING,
      payload: {
        message: e.message,
        statusCode: e.statusCode
      }
    });

  } finally {

    yield put({
      type: types.REQUEST__FINISHED,
      payload: {
        requestFrom: 'my-saga'
      }
    });

  }
}

export function *watchRequestProfile() {
  yield* takeLatest(types.PROFILE__REQUESTED, doRequestProfile);
}

I have highlighted the problematic line.

The issue here is that the error in the browser is fairly cryptic. It took me digging into the source code to figure this one out.

But actually the issue is really simple to fix.

The highlighted line above should be :

const profile = yield call(api.fetchProfile, action.payload.accountId);

I was calling my function myself, and then passing the promise on to the Saga… whoopsie daisy.

How I Fixed: unresolved variable or type await

Ok, super easy one here, but as a newbie to async / await this did catch me out.

Let’s pretend we have a function:

export function login(username, password) {
  const requestConfig = {
    method: 'POST',
    mode: 'cors',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ username, password })
  };

  const loginResponse = await fetch('https://your.api/login', requestConfig);

This will present an error in WebStorm, and Googling came up only with a bug report from 2015.

In my case, WebStorm would give two different error messages:

webstorm-async-await-expecting-newline-or-semicolon

orwebstorm-async-await-unresolved-variable-or-type-await

Ready to kick yourself?

export async function login(username, password) {
  const requestConfig = {
    method: 'POST',
    mode: 'cors',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ username, password })
  };

  const loginResponse = await fetch('https://your.api/login', requestConfig);

This I tell you brother, you can’t have one without the other 🙂

 

An Example of JavaScript Double Negative

javascript double negative
Image credit: https://flic.kr/p/2oNKyk

I remember the first time I saw JavaScript double negative syntax. It confused the heck out of me – so much so, I thought it was a mistake.

Over time, I have seen other developers equally confused. It must get asked in Slack at least once a quarter, if not more.

In case you aren’t sure, let’s quickly cover exactly what we are talking about here:

{ isAuthenticated: !!localStorage.getItem('idToken') }

The point of interest here being the double negative / double bang / !! / double exclamation marks. I put all the alternatives I can think of in, in the hope that it helps others searching for similar.

It’s also worth pointing out that double exclamation marks are not a single operator. I guess that makes it all the more difficult to Google, and understand.

Just Tell Me What It Is

For the impatient, using Javascript double negative / not not will coerce your value to a boolean, and ensure that the output is of type boolean.

To translate that from technobabble:

The first ! will convert a value that isn’t either true or false into a value that is  true or false, and then invert it.

The second ! will invert it again.

Confusing?

An example illustrates a potential use case, which hopefully will make it slightly easier to understand.

Just Show Me An Example

Here is a real world example from an authentication reducer I am currently working on in a React / Redux project.

Let’s look at the code as it stands before adding in the double negatives:

import {
  LOGIN__FAILED,
  LOGIN__SUCCESS,
  LOGOUT__SUCCESS
} from '../constants/ActionTypes';

export default function auth(state = {
  sendingRequest: false,
  isAuthenticated: localStorage.getItem('idToken')
}, action) {

  switch (action.type) {

    case LOGIN__SUCCESS:
      return Object.assign({}, state, {
        isAuthenticated: true,
        userId: action.userId,
      });

    case LOGIN__FAILED:
      return Object.assign({}, state, {
        isAuthenticated: false,
        errorMessage: action.errorMsg
      });

    case LOGOUT__SUCCESS:
      return Object.assign({}, state, {
        isAuthenticated: false
      });

    default:
      return state;
  }
}

Most of this is not that interesting in the context of this particular example.

I have highlighted line 9 where localStorage is used to retrieve the user’s idToken, should it exist.

There is a bug in this code.

The default case of the switch statement means that when a user first loads the page / before they have ever logged in, the default state would return:

{
  sendingRequest: false,
  isAuthenticated: null
}

Instead, I want it to be:

{
  sendingRequest: false,
  isAuthenticated: false
}

I could use a ternary operator to fix this:

export default function auth(state = {
  sendingRequest: false,
  isAuthenticated: localStorage.getItem('idToken') ? true : false
}, action) {

And this would work, but WebStorm moans at me that I can do better:

webstorm-can-be-simplified
WebStorm telling me I suck

I make the change:

export default function auth(state = {
  sendingRequest: false,
  isAuthenticated: !!localStorage.getItem('idToken')
}, action) {

Why does this work?

Because by localStorage.getItem(‘idToken’) is going to either return me a string (containing my locally saved idToken), or as we have already seen, a null.

If we check whether a string is not (!) something, we would get false – because a string is definitely something.

It therefore stands that if a string is not not something, then it must be true.

Likewise, if we check if a null is not (!) something, we would get true – because a null is definitely nothing.

It equally therefore must stand that if a null  is not not nothing, then it must be false.

Still confused? Don’t feel down about it. It took me ages to grasp this, and it’s so unusual that it really won’t come up that frequently.

I would even go as far as to say consider sticking with the ternary operator approach. It is simply easier for most developers to immediately understand, even if WebStorm moans about it.