blog

The Design Sprint

Introduction

The design sprint was created by former Googler, Jake Knapp. It’s a time-bound process, with five phases typically spread over five full 8-hour days. The goal is to solve a critical design challenge through designing, prototyping, and testing ideas with users. The benefits of a design sprints include: saving time, creating a path to bring a product to market, prioritizing the user, and testing a product before launch. Before starting a design sprint, ask yourself: Are there any potential solutions to your design challenge? Are cross-functional teams needed to weigh in on your design challenge? Is the design challenge’s scope wide enough for a sprint? If you answer yes to any of these, a design spring might be the right move.

The Five Phases of the Design Sprint

Each phase takes one full day, is hands on, and requires creative collaboration. The five phases of a design sprint consist of the following:

Understand. This phase sets your sprint on the right track and helps your team get a clear picture of the design challenge by having conversations with other people, like experts in different departments and industries. In this and all phases, you focus on the user.

Ideate. Come up with ideas and build off of them to create solutions. Sketch and present these ideas. Prepare for testing. Start your target profile with an inclusive approach.

Decide. Which solution do you want to build? Discuss each possible solution and select one that is most likely to excite users and achieve the design process goal. Create a step-by-step blueprint for the prototype phase.

Prototype. Build the first version not the finished product; something realistic enough to test with users. Confirm the test schedule, finalize interview questions, and make sure the prototype is ready to go.

Test. Put the prototype in front of users. Observe how they react and interview about their experience.

Benefits

Design sprints put the user first. Throughout the creative collaboration, every person is valued and the best ideas rise to the top. The time is distraction-free, giving time for collaborators to focus. By running a design sprint, the team lowers their risk of an unsuccessful market debut. It can be scheduled at any point during a project.

My Design Sprint Experience

In my experience, I have rarely come across companies who implement design sprints. I have seen all five phases, but they lacked this scheduled, 5-day timeframe, and the collaborative approach. The ideate and decide phase would for example only involve the project owner and key stakeholders, or the UX designer, product manager, and client. Phases would stop and start during intermittent meetings scheduled across weeks. Looking back on my time as a web development manager, I attribute the great success of our flagship product to the collaborative approach we took across departments, valuing every voice. It not only brought the best ideas to the fore, it also built a strong sense of teamwork and individual satisfaction. However, it could be difficult to organize all of those voices across the span of a year, and the design sprint approach would honed those contributions into succinct focus. It would be fantastic if more companies and collaborators adopted the design sprint into their workflow.

A design sprint would help me design with the user first. By talking to experts in the Understand phase, and utilizing my team members and their diverse perspectives, my understanding of the problem we’re trying to solve and its potential solutions has a much better chance of solving the problem than solving it with a limited team in a silo. The Ideate phase allows everyone’s ideas to be heard, and even if a lot of the ideas might be bad, it ensures that all of the cool ideas are heard, too. Similarly, the Decide phase has everyone vote, and the solution that has the most potential is chosen. With this clarity, preparation for the Test phase will lead to appropriate testers, survey and interview questions, and necessary equipment. The Prototype phase is massively hands-on in creating, asking questions, offering ideas, and reviewing the completed prototype. Details won’t get missed or forgotten thanks to the sprint’s condensed timeframe. The Test phase collects user feedback by observing and interviewing users. I think I’d have the most interest in this phase, because it’s a moment of truth: were we successful in putting the user’s needs first? Again, this will determine whether the solution is a success based on the user’s experience, rather than a single person or a very limited group of decision-makers’ assumptions, which means a higher rate of success at the product’s release.

In design sprints and otherwise, it’s good to take a retrospective on what went well and what could be improved. In my projects, I have found the best approaches have been to share all my ideas and create an environment where others are comfortable and confident to do the same. The only bad suggestions are the ones not shared! This created the strongest solutions because we harnessed the brilliance from our whole team. However, there could be frustration if someone missed a meeting or notes were not taken so potential ideas fell through the cracks. Next time, I would much rather take this 5 day design sprint approach to dedicate everyone’s time together, without losing any momentum.

I recently worked on projects at a large organization that would have benefited from a design sprint. The Understand, Ideate, and Decide phases involved the key stakeholder requesting a product to the product owner, who tried to gather as much information as possible. The PO took this request to the development team, who usually needed more information from the key stakeholder. Time was spent circling back, but the PO did not fully grasp the developer’s needs, nor, as a result, did the key stakeholder. Furthermore, the product was for an end user that was not the key stakeholder, and there was no research done on the end user’s experience. The project would have benefited from an Understand phase where the team could interview the key stakeholder, as well as any experts on the end user’s experience, such as the customer service representatives.

Developer Vernacular

As a freelancer, I often work on my own from start to finish on any given project. It’s common that the only people I explain my work to are my clients, or designers, marketing specialists, and managers. In these cases, I use common vernacular or business and marketing terms to describe the intent, purpose, and functionality of the product. There have been times I have become so accustomed to this approach in communication that I find myself tongue-tied when talking with developers. There are a myriad of terms, expressions, and vocabulary used exclusively within the realm of developers, and when one doesn’t find themselves needing to use it often, those phrases can get lost. Therefore, I’m creating a series of posts to help describe terms and phrases that were not originally apparent in meaning to me, and could be useful for others reading here. These days, I mainly work with React and JavaScript, and these terms will work within that framework and language, but you will find most extend across languages.

“Lifting the State Up” or “Lifting State Up”

React has a component-based architecture approach, where components can be children, parents, or even siblings (children of the same parent component). Data – or state – can be passed from parent to child, but what about from child to parent, or sibling to sibling? In that case, we need to lift the state up. Lifting the state up can be passed via props. If passing from sibling to sibling, it should only be lifted up to the nearest parent component and then down to the child needing the data.

Array Destructuring

One of the features of JavaScript ES6 is array destructuring and it’s become ubiquitous in React since the advent of hooks in v16.8. Let’s take the useState hook as an example. The syntax for this hook looks like the following:

const [count, setCount] = useState(0);

What’s actually happening here? useState returns the current state and a function that updates it. So in order to initialize both values in one line, we use array destructuring. There are other methods to assign values within an array, but they usually require more lines of code, and therefore we can consider array destructuring as “syntactic sugar”.

Syntactic Sugar

Rounding out this little list of developer jargon brings us full circle, back to comprehensible communication, and code is no exception! These days, there are languages and applications that make coding quite close to our common tongue. New languages and updates to existing frameworks often strive to develop syntax that is easy to comprehend, write, and read. Syntactic sugar is the phrase used for this sentiment.

Conclusion

The world of code is vast, there’s always something new to learn, the concepts can be tough to grasp and convey. If you find yourself frustrated by all of the terms, have patience. Just as coding can become second nature, with enough exposure, these terms start to feel more and more common. If you freelance or work as the only developer in your department and have few people to discuss your code with, I have found online video courses to be particularly helpful. While there are lots of resources to read from, I find having the audio element of a teacher who explains the terms out loud really helps me remember and lock in terms used when communicating developer-to-developer. Keep a beginner’s mind and stay curious!

Applying Constructivisim: Asana Example

Introduction

In this example, I’ll be adding onto my behaviorism learning scenario and add an additional learning objective in which Constructivism is the primary learning theory that drives the activity. Constructivism focuses on students taking an active role in learning and developing new knowledge related to their existing knowledge, often through collaboration. I will also be describing the skills in the Zone of Proximal Development. Finally, I’ll discuss a scaffolding strategy and a social constructivist strategy to help learners take an active role in constructing their own knowledge.

In the initial scenario, students were taught a sequence of exercises, presumably through video or written instruction, and demonstrated understanding through an online quiz. Automated verbal feedback was provided with each answer and in the final evaluation. However, there was no social interaction or reflection on the student’s own experiences and was devoid of any constructivist approach.

A Change in Key Assumptions

A key assumption of constructivism is that what the student currently believes, whether correct or incorrect, is important. As a yoga teacher, I know that a student’s understanding of their own body is just as, if not more important than the poses and sequences I teach. Therefore, I would want to add to my asana example with a way for the student to reflect on their own experiences with the poses and sequence, where they can challenge and even reject the new information if it challenges their prior knowledge. In turn, I would respond to and have a dialog with the student regarding their approach. I have undergone yoga training where the word of the teacher was not to be questioned; they were the “expert” and I was supposed to be an empty vessel to pour knowledge into. However, I have seen over the years that there every body is different, and to teach and practice safely, we ought to provide an environment where a student feels safe to challenge the status quo. In constructivism, both the teacher and student should think of knowledge as a dynamic, ever-changing view of the world we live in and the ability to successfully stretch and explore that view, not as inert facts to be memorized.

Utilizing Scaffolding

In this scenario, I will not refer to the Zone of Proximal Development with a metric of age, as we see in Vygotsky’s papers. Instead, let’s think of the student’s ability to enter a pose the way it was originally taught. There may be a number of reasons why they cannot enter the pose: they do not fully understand the cues or the transitions, their range of motion does not have access to the pose, or their skeletal structure cannot replicate the pose, to name a few. I can use a scaffolding with a social constructivist strategy to help the learner take an active role in constructing their own knowledge.

Processes that aid effective scaffolding:

  • Gain and maintain the learner’s interest in the task: Engage the student in a verbal dialog with their own evaluation of the practice.
  • Make the task simple: Understand the student’s range of motion and how far they can go in the pose or sequence.
  • Emphasize certain aspects that will help with the solution: Supply modifications within the pose or sequence.
  • Control the student’s level of frustration: Make it abundantly clear that modifications are normal and necessary for most practitioner’s of yoga.
  • Demonstrate the task: Provide the same visual references for the modifications as I did in the curriculum that demonstrated the original poses and sequence.

Equity-Focused Design

While this approach is effective, it has not been designed inclusively. Rather, the original curriculum should have included a reference to modifications. Inclusive design thinks about benefiting everyone from the beginning, not as an afterthought. Therefore, modifications to the poses and sequence should be presented with the original curriculum. This does not change the scaffolding exercise. The dialog can still be included because there are as many bodies as there are people, and a modification for one person may still need to be adjusted for another.

Social Constructivist Strategy

To apply this learning experience with a social constructivist strategy, the students could break out into groups of 2 and teach each other what they have learned. They can take turns leading, applying four cognitive strategies:

  1. Questioning: Start by asking or evaluating if the other student needs adjustments.
  2. Summarizing: Teaching the sequence as the student follows their instruction.
  3. Clarifying: Adjusting their teaching based on cues they may notice in the other student.
  4. Predicting: Based on the feedback they have received from the other student, either in the initial questioning phase or by noticing their depth of range in their practice, adjust the sequence as appropriate.

This creates a zone of proximal development where students gradually assume more responsibility for material through collaboration, forging group expectations for high-level thinking and acquiring knowledge that is vital for their learning and success in everyday life and the practice.

Adding Authentication To A React App

Introduction

Do you need authentication in your React app? Only if your website has content that should be protected. Sometimes you may have content that should not be visited by all users or visitors of your site. You may also have pages that can only be visited if a user is authenticated. For example, a profile page should only be visited if a user is logged in. If they are not logged in, the profile page should not be accessible. However, pages are not the only content you may need to protect. You may want to protect data that you’re storing in a database, API endpoints (like a change password request) that you only want accessible for authenticated users, and more. Authentication is handy if you want pages inaccessible when a user is not authenticated, and also if you want to keep endpoints that can, for example, change a user’s password accessible only for authenticated users. There are many more examples, of course, but let’s dive in further.

How It Works

Authentication is a two-step process. Think of it like a lock and key. If you have a key, you can gain access anywhere the lock matches the key. Similarly, in authentication, you log in with credentials (the key), which gets sent to a server and verifies your credentials in a database (the lock). If valid, you have permission to enter, and that permission persists as you navigate the site. In our example earlier, you need permission to log in, but you also need permission to access the profile page. Using API endpoints, we use the permission granted at log in to help us access other protected parts of the application. However, the permission granted can come in different ways, either server-side or with authentication tokens.

Server-side authentication is a very traditional method where the server stores a unique identifier for your client after the access has been granted, and this identifier is also sent back to the client. That way, as you navigate the site, this identifier is sent back to the server from the client, and because it is unique, the server can verify the request. However, this method comes with a significant disadvantage, particularly for single page applications (SPAs). If your SPA is hosted on one server, but your REST API is served by another server, your front end is not tightly coupled. Consider an API that is used widely, like the Twitter API. Obviously this API can’t be tightly coupled to your server and your one specific front end. It needs to be flexible, and therefore you can’t store an identifier on the server as it needs to remain stateless. It should not be storing this kind if data about its connected clients.

With SPAs, you will often work with decoupled back end and front end setups, and here you use authentication tokens. While you are still sending unique identifiers between the server and client, the identifier itself is created, stored, and utilized in a different way. First, when the client asks for permission, it sends the credentials to the server, where the credentials are still verified and stored. The server then creates a unique token based on a key that it uses to encrypt some data based on the user and sends this token back to the client. Generally, these are created in the “JSON Web Token” format, and are essentially long strings of encoded data created using the server’s private key. Instead of storing a unique identifier on the server as we saw in server-side authentication, this token gets stored on the client. Therefore, whenever the client needs further access (when navigating to a protected page, for example), the client sends this token back to the server, and the server checks it by determining if the token could have been created by the server with the server’s private key. Below, I will outline my process in implementing this approach.

Implementation

For my setup, I used Firebase as my back-end with the Firebase Authentication REST API. The documentation is very clear with API endpoints for most authentication needs, like creating a user with email and password, logging in a user, or getting access to protected resources like changing a password. Its of course possible to write your own back-end code, but that would have been overkill for my project’s needs.

To get started, you will need to create a Firebase project and unlock authentication. You have options when choosing your sign in method. For my example, I used email and password.

Sign Up

When creating your sign up form, you will need to send the email and password credentials to the dedicated email/password sign up API endpoints. Create a submit handler function that is triggered when the user submits the form. Prevent the browser from sending a request automatically by calling event.preventDefault(), because we will be sending our own request. To extract the entered data, you can log every keystroke using state, but I used refs with connected refs to the input elements. Create your ref hooks with useRef() and then set the ref attribute for the input elements. In the submit handler, you can extract the values of those form elements by using the ref’s value, for example emailInputRef.current.value. Here is where you would also add validation to ensure you have a real email address and a secure password.

You currently don’t have sign up or log in setup. Eventually both of these will use the same logic with different API endpoints, but for now, start by setting up the sign up functionality. Find the sign up with email and password API route in the Firebase Auth REST API documentation. You will see that it is a POST request to the URL and what data is attached to create a new user. Take this URL and send that HTTP request using the fetch function. You may want to use custom hooks or Redux here, but in this example we will call fetch directly in the authentication form component. When adding the API endpoint, you will need to edit it with your project’s specific API key. This points Firebase to your project, where you want to create a new user. Create the second argument of the fetch function by passing in an object that describes your request. Again, the method will be a POST request. The body will be in JSON format and you will need to set an email, password and returnSecureToken which should always be set to true. Add a content type header to the outgoing request that is set to JSON. So now we have a request that is ready to send, but what will we do with the response? We will need to handle errors and do something with the data that is sent back. To that end, add a catch to the fetch function in order to handle errors, and a then to handle the response. You can also handle errors in the response data, because this data will hold additional information if the response is not successful. You can use async await to handle this, too, but I have used a nested promise chain that checks if the response and returns the JSON data if it is ok and throws an error if it is not. Connect the submit handler to your form and you should now be able to create a new user. You will not be able to test this on your app yet, because you have not added log in functionality, but you can check your Firebase database and see the user you have just created through your form.

You may choose to use the error messages that come through the response data (data.error.message) or setup a general “Authentication failed” message for your error handling, but in either case it is a good idea to setup an error handling message as part of your form. To further a good user experience, you may also want to show a loading state while the request is sending. Do so by creating a piece of state isLoading with setIsLoading as its state updating function, originally setting it to false. Set it to true just before you begin the fetch method. Once you have received a response – so within the then block, you can set it back to false. Now you can show the submit button and a loading spinner conditionally based on whether isLoading is true or false.

Log In

Checking the Firebase Auth REST API documentation for logging in with email and password, you will see that you are sending almost the same request as signing up. The method, body, and headers are completely the same, and the only difference is the API endpoint. For this reason, DRY (Don’t Repeat Yourself) and share the same code where possible. First, create a isLogin state that toggles true/false depending on if you are on the Sign Up or Log In form. I just created the same form and showed the title, button, and link to the other form with their text values dependent on whether isLogin was true or false (for example, the title reads “Sign Up” when isLogin is false and “Log In” when isLogin is true). Create a helper variable for the API endpoint URL and assign it within an if/else with the conditional being isLogin. Place the fetch block immediately after this, and replace the hard-coded API endpoint with the URL variable. Now your request should succeed for both Log In and Sign Up. However, let’s handle the response, because even though the response data will be different if you login or sign up, you will receive an authentication token in both responses. So in your nested promise, you first check if the response is ok, and if it is, then return the response JSON data. If it is not ok, then throw a new error. Now you want to add another then block to handle the response data and a catch block to handle the thrown error. You can test this by doing a logging the response data in the console, or displaying an error in an alert box (try signing in with incorrect credentials, or, if you haven’t setup validation on the password, Firebase will send an error if you trying signing up with a password that is less than 7 characters). Your response payload will include the ID token, which is the authentication token, and now we will be able to use that in future requests.

Storing the Authentication Token

Once you’re signed up or logged in, your front-end interface and user experience will probably change significantly across the site. A lot of components, like your navigation, and access to protected resources, like the profile page, will need to know whether the user is logged in or not. To manage this app-wide state, you have a couple of options, including Redux or the context API. For this example, I am using the context API. This way, I do not need to download another third-party package, I know that my authentication state will not change very much, and therefore I don’t expect performance issues.

To add the context API, add a JavaScript file where you will set up and manage the context for the authentication data. You will create your authentication context with React.createContext, initializing it with data that will define the context and allow for better auto-completion. In your initial data you will have the token set as an empty string, an isLoggedIn boolean set to false, a login function that passes in your token to an empty function, and a logout function that passes in and does nothing at the moment.

Now create the provider component for the authentication context you have just setup. This will serve as a wrapper around your entire app and will manage its authentication state. Therefore, it will receive the children as props. As this component is managing state, use useState and for creating and setting the token. With this token state, we will be able to determine whether the user is logged in or not. To set a boolean showing if the user is logged in, use !! in front of the token you just set in useState. This checks if the token exists and converts it to a truthy or falsy value. Finally, you will need handlers to change the token state, so create login and logout handlers. In the logout handler, set the token to null again. The login handler will need to accept a token as an argument and then be set to that incoming value. Now export the context as default and export the provider as a named export, go to your app’s file, and wrap the provider component around your entire app.

Now you can tap into this context component from the entire app using useContext. Go back to your authentication form and create the authentication context (something like const authenticationContext = useContext(AuthenticationContext)). Now you are able to pass the token received in your response data on the login of this context. Now that you have set the token, the sky is the limit for your UI. You can conditionally render content based on whether your isLoggedIn in your context is set to true or not. You can also tap into the context if you need the ID token, which would be a necessary part of your request body when, for example, sending a request to the change password. If you would like to redirect the user when logging in, signing up, etc., you can use the Redirect component, which is part of React Router.

Log Out

You may be able to determine what logic is needed at this point in order to log out. What is the app using to drive the user’s logged in experience? That’s right: the authentication token. So we don’t actually need to do anything with API endpoints and any conversation between the client and the server. All we need to do is clear the state that holds the token, which of course we have stored on the client. Therefore, wherever logout functionality is needed, you need only access the authentication context provider and bind the logout handler function that we setup before. Once the user is logged out, you may want to redirect them away from the page they were on, especially if it was a protected resource.

Access Protected Resources When the User Is Logged In

When logged out, there are a number of pages we probably don’t want a user to access. Let’s go back to the profile page example. If I know the URL to a profile page, I certainly shouldn’t be able to just type it in and access it if I am logged out. So how do I protect that page, that resource? We use navigation guards.

Navigation guards are rather simple, actually. We simply change our route configuration whether the user is logged in or out. Most likely your routes are determined in your main component, and this is where you setup the navigation guards. Return to the authentication status you used to conditionally render the UI, the isLoggedIn boolean you setup in the authentication context. Just as you conditionally rendered parts of your UI, you will do the same for your routes. You can also redirect the user if they try to access an unauthorized page, perhaps to a 404 page. In this case, I will redirect them back to the home page with the help of the react-router-dom redirect component.

<Route path="/profile">
  {authorizationContext.isLoggedIn && <UserProfile />}
  {!authorizationContext.isLoggedIn && <Redirect to="/" />}
</Route>

Authentication Persistence

A major consequence of storing your authentication token in the client is that its state will reload every time you reload the browser or manually enter a URL to your app because you are restarting your app, losing the context, and reverting back to its initial state. Of course, that’s not ideal and your user would most likely expect to still be logged in. You are probably used to remaining logged in after signing in, but after some time, you are logged out. That is because tokens expire after a set duration, often defaulting to one hour. This is a security mechanism. Fortunately, we not only receive the authentication token as part of the response when signing up or logging in. We also receive “debt duration”, the time left where the token will be valid. While it is possible to refresh the token, here we will work with the debt duration data and log the user out once it has expired.

There is still one more problem: the token is being stored in state, and that will be cleared every time the page reloads. Fortunately, browsers have other means of storing information. For one, cookies, but we will use an even easier mechanism which is local storage. It’s worth understanding both options. Neither are fool-proof solutions and for posterity’s sake, it’s worth noting that local storage is vulnerable to cross-site scripting attacks. Nevertheless, we soldier on. So let’s store in local storage, which stores simple information that survives page reloads. Then when we load the app, we can check to see if that information exists so we don’t have to force a new request to the server.

You will use localStorage.setItem() to set the name and value, or a key/value pair, of the the token. This uses an API that’s built into the browser, which you have also used with the fetch function. Use localStorage.removeItem() in your logout handler to clear the token.

Now when you start the app, you will want to see if there’s a token in your local storage. If it exists, you will initialize your state with that token, which will then continue on with the logic you setup for authenticating the user. Previously, we were initializing the token state as null, but now we want to check if the token exists using localStorage.getItem() and then setting that value in our useState() hook. The token may not exist in our local storage, but in that case it will appear as undefined and still work here, because local storage is a synchronous API.

Logging Users Out Automatically After Some Time

There’s one issue remaining that needs to be solved. As mentioned earlier, the issued token comes with an expiration date. In our logic, we have set a token in our user storage, but what happens when its expiration date has passed? No logic has cleared it from storage at that time interval, so the app still believes it has a valid token. However, if you were to make any calls back to the Firebase Auth API, and that call needed a token (for example, if you were to change your password), you would no longer have a valid one and the call would return an error. We need to clear the token once the duration date has passed so that our app remains in sync with the tokens issued by the server.

To do this, we need to store the duration date alongside the token in local storage, and then calculate the remaining time by subtracting the current time from the duration date. First, calculate the expiration time when you are passing in data from the authentication API by adding the expiresIn time to the current time. Pass this to your login handler function in the authentication context file. Now in your authentication context file, add a new helper function which takes this expiration time as an argument and calculates the remaining time by subtracting the expiration time from the current time and returns the remaining duration. You will be calling the logout handler when the token has expired using setTimeout(), so be sure to calculate in milliseconds.

Now we need to setup the timer that will logout when the remaining duration is zero. In the login handler, create a variable that is equal to the returned value of the helper function that returns the remaining duration value when you pass in the expiration time, which you have here because you are now passing it into the login handler function when receiving data from the API. Use setTimeout() to call the logout handler when that time has expired.

We must also clear it if the user logs out manually. Create a global variable for the timer and set it to the timer you have just created. Now in your logout handler, check if the timer variable has been set, and if it has been, clear the timer using clearTimeout().

You must also clear the token you have stored in local storage if the timer has expired, of course. Create another helper file in the authentication context and get the token and expiration time from local storage (you can set the expiration time in local storage in your login handler). As you did before, calculate the remaining time using the helper function you used and passing in the value stored in local storage. If the remaining time is less than or equal to zero, then remove the token and expiration time from local storage and return null. If there is still time, then return the stored token and stored remaining time. Initialize the token with the stored token value (under the condition that it exists). Use useEffect() with this data as a dependency and then within the function, check if the data exists, and if it does, set the timer again with the data’s remaining time. Be sure to also remove the remaining time from local storage in the logout handler. Now we have set the user’s remaining time if we have automatically logged in the user.

Conclusion

Unlike many tutorials, I have not supplied most of the code here. This walkthrough is mostly a way to read through a potential solution for adding authentication to your project using tools available in your client and React. There are almost always different ways to go about finding solutions and so I wish you happy coding!

Learning Method Overview: Behaviorism

It was suggested by J.B. Watson that psychology be studied through objective, observable behaviors rather than subjective, internal thoughts and consciousness. This was in opposition to the historical development of psychology. In the late-19th and early-20th century, introspective psychologists like Wilhelm Wundt maintained that the study of consciousness was the primary object of psychology. Their methodology was primarily introspective, relying heavily on first-person reports of sensations and the constituents of immediate experiences. Watson proffered that experience and environment (rather than internal motivations or inherited traits) dictate who or what a person becomes (how he behaves).

Watson was expanding upon Ivan Pavlov’s findings of classical conditioning. Pavlov discovered classical conditioning or stimulus-response when he noticed that after presenting food and a normally neutral stimulus (ringing a bell) together repeatedly created the response to the stimulus (salivating for food) as a result of the neutral stimulus alone. Two separate conditions create new synaptic relationships to occur between the stimulus and response.

Behaviorism was popularized by B.F. Skinner in the 60s or 70s. Skinner had discovered operant conditioning, where the subject learns from the consequences (reinforcer or punishment) of its own behavior. Behaviorism supposes that psychology is more aptly studied through observing behaviors of individuals and making connections between their behaviors and their environments or prior stimuli.

As a learning method, behaviorism relies on “skill and drill” exercises to provide the consistent repetition necessary for effective reinforcement of response patterns. Other methods include question (stimulus) and answer (response) frameworks with gradually increasing difficulty, guided practice, and regular reviews of material. It relies heavily on positive reinforcement such as verbal praise, good grades, and prizes. Exam performance can assess the degree of learning because it measures the observable behavior. Behaviorism proves most successful in areas where there is a “correct” response or easily memorized material, like teaching facts or formulae, scientific concepts, and foreign language vocabulary. It however has questionable effectiveness in teaching comprehension, composition, and analytical abilities.

Methods of learning when there is one correct response to reinforce includes:

  • Discrimination: Identify whether a concept belongs to a specific category. Drag-and-drop exercises.
  • Generalization: After identifying the attributes of an item belonging to one category, assign attributes to all items within a category. Teaching through example.
  • Association: Link a specific stimulus to a specific response. Matching exercises.
  • Chaining: Sequence ordering exercises with a predefined and unique correct sequence that users must form. Very common to find through trial and error.

Feedback is important not just at the end of the eLearning course, but each time the learner interacts with the system. Positive comments, punishment, negative criticism (not quite acceptable today nor appropriate for adult learning), and negative scores are all examples. In gamification, examples include assigning points, grades, badges, leaderboards, removal of benefits like points, lives, etc.

Behaviorism has received a lot of criticism over the last few decades because it does not take into account other aspects of learning such as the mental processes involved or the environment in which learning takes place. However, I can still see behaviorism under a UX lens, for example, in the testing phase while using analytic tools that report on user behavior. The introspective methods prior to behaviorism could alternatively be seen while using surveys or interviews during user research.

Behaviorism as a learning methodology could be seen as showing a notification when a user receives a message. When the user sees the notification, their physical response begins to connect to that social connection, and we see a dopamine response begin to associate with the notification. When removing notifications, the response can become extinct and the user may no longer be addicted to their phone.

Applying Behaviorism: Asana Example

Teaching yoga asana, the physical postures (such as downward dog and child’s pose) that are sequenced together to create a practice that strengthens and stretches the body, can be taught in asynchronous online learning through the learning theory of behaviorism. Take, for example, a sequence of poses that are meant to be practiced in a specific sequence: from the head to the toes. Each section of the body, for example, the shoulders, has its own group of exercises, such as shoulder rolls and shoulder flossing. Because this sequence follows a particular order, and includes groups of sequences that can be classified by sections of the body, it qualifies as knowledge that can be learned through behaviorism because behaviorism proves most successful where there is a “correct” response or easily memorized material.

We could create a mini eLearning module that teaches the correct sequence and the exercises for each section of the body. Then, we test the learner in a quiz with the following methods of learning:

  • Discrimination. Using a drag-and-drop exercise, have the user drag the exercises listed to the correct section of the body.
  • Chaining. Sequence ordering exercise with a predefined and correct sequence of exercise that the users must form. Supply a list of exercises and have the user order these correctly.
  • Feedback. Supplement positive comments each time the learner provides a correct answer. Reward the user with a video showing the full sequence after successfully completing the quiz.

The stimulus is the learning material and its subsequent reinforcement via the discrimination and chaining exercises. The response is the feedback and reward when successfully answering and completing the quiz. The negative reinforcement is the absence of positive feedback when unsuccessfully answering the questions, because negative criticism is not acceptable in this learning context.

Behaviorism is focused on observable behavior, and a quiz is a common method of assessing the degree of learning in behaviorism as it can measure observable behavior by the user’s score.

Positives using this approach is the ability to teach a physical practice using methods alternative to physically demonstrating. Perhaps the user is unable to demonstrate a particular exercise, but can still demonstrate comprehension. It also reinforces the correct information through language rather than strictly through physical demonstration.

Negatives using this approach include the inability for the user to question alternatives. For the user who may be unable to demonstrate a particular exercise, this approach lacks information to help them make modifications or adjustments in their practice. Similarly, while a successfully completed quiz demonstrates comprehension of the correct sequences and classifications in the practice, the instructor cannot determine if the user can actually physically perform the yoga asana practice. Supplementing with a field where the user can ask questions or upload videos of their practice that a teacher could review would not fit within the bounds of a behaviorist learning theory, but could prove to be a better experience.

Identifying Good UX Design: FoodieLand App

Exemplar image from Google’s Foundations of User Experience Design course on Coursera.org

Prompt 1: Identify at least one aspect of the FoodieLand app that demonstrates usable design. Explain your reasoning in 1-2 sentences. 

Hint: Is the app’s design, structure, and purpose clear? Does the app have any elements or features that make it easy to navigate?

Usable
The placeholder text clearly demonstrates what in particular the user would search with the label “Search food”. The icons for filters and adding or subtracting items are ubiquitous.

Prompt 2: Identify at least one aspect of the FoodieLand app that demonstrates equitable design. Explain your reasoning in 1-2 sentences.  

Hint: Does the app address the needs of people with diverse abilities and backgrounds? 

Equitable
Icons that allow the user to select language and audio playback demonstrates usability for non-English-speaking users or users with vision impairments.

Prompt 3: Identify at least one aspect of the FoodieLand app that demonstrates enjoyable design. Explain your reasoning in 1-2 sentences.  

Hint: Does the app inspire a positive reaction from the user by considering their thoughts and feelings? Does the app engage users and make them excited to keep using the app?

Enjoyable
The ratings side-by-side with the pricing makes it easy to determine a more informed selection when choosing an item.

Prompt 4: Identify at least one aspect of the FoodieLand app that demonstrates useful design. Explain your reasoning in 1-2 sentences.  

Hint: Does the app solve the problem of “how to help a busy person working from home select a meal to be delivered?” How does the app help solve this problem?

Useful
There is very little text to navigate and it is easy to scan the screen quickly, allowing for a fast experience to solve the user’s problem of getting food.

A user experience you think is great and why

Ikea’s Maximara inner drawer is an innovative twist on the sock drawer. It is a drawer that sits inside of a larger drawer. It is usable as it’s mechanical functionality functions identically to the drawer which contains it. The assembly user manual is equitable in demonstrating how to build the drawer with pictures rather than a limited set of languages. I find it enjoyable because the rubber surrounding the wheels that sit in the tracks of the drawers creates a silent slide in and out of the dresser. I find the inner drawer particularly useful in containing everyday items I reach for when I am dressing. I used to forget about accessories like jewelry, but now I remember it as an option when I am choosing my socks.

A user experience you think needs improvement and why

My English/Danish dictionary has revealed necessary improvements in order to enjoy it fully. It is not usable in providing past or future verb endings as I have found in larger dictionaries. I don’t find the use of the Union Jack to demonstrate the English language particularly equitable, as it is a language spoken around the world. I have been disappointed to find there are some words I have not been able to find, due to it’s limited size. Only some of the words have info pertaining to type of word, i.e. adverb or adjective, to differentiate words with identical spelling but different meaning. However, I would find it more useful if this info was provided for every word.

Self-Reflection: Your journey to become a UX designer

What skills do you already have that can help you on your journey to becoming a UX designer? For example, are you artistic, detail-oriented, or considerate? Do you have relevant past experience in the world of design? Share your passion.

I have experience interviewing many clients to understand their pain points and how to establish user trust. I have also interviewed my clients for my own business in order to understand how to formulate my ideal customer avatar and position my brand to their needs. I also have experience storyboarding for animation, as well as wireframing for websites. I have used visual design across mediums, from fine art to space to digital, utilizing color theory, typography, and design theory to create an informed visual experience. Additionally, I have working experience as an engineer, with experience communicating to and as an engineer. As a web development manager, I have utilized communication to ensure clear and timely communication, and deciding the scope of the product.

What are your goals for exploring the field of UX design? Why do you want to pursue this professional certificate?

My goals for exploring the field of UX design is to have a formally educated review of the field, so I can make the most informed decision on the next best steps for my career. Since I already use UX design, I also want to hone my craft and utilize best practices based on the information received here. I want to pursue this professional certificate less so for the recognition from potential employers. I am doing this in order to stamp out the imposter syndrome that wonders if I have all the information I need to apply to the jobs I really want to tackle, to stamp out the voice that questions if I have enough in my portfolio to apply. I believe that pursuing this certificate will give me recent, tangible work to show, which will be supported by my experience undertaking similar work, now leveraged with the best practices learned here.