Designing and building software is all about tradeoffs. As a developer, I usually begin the planning phase by mapping out the ideal way I would like to interact with the software. If this is an iOS application, I ask how I would like my program to behave with certain input, what does it do when I swipe left? How about swiping right? If this is a new library, I map out how I would like to interface with this library. After mapping out the ideal interface and interaction, then the realities of actually building that ideal creep in.
At First Opinion, we decided early on that passwords were annoying on a mobile device and so our app would use uniquely generated tokens when signing up on the app. Basically, a user would sign up using their device and then their account would be locked to that particular device, they would always have access to their account (on that particular device) and they would never even have to think about a username and password. Then, if they ever tried to use First Opinion on another device, we would see it wasn't their validated device and send them an email with a registration code that they could then enter into their new device to tie their account to that new device. No usernames or passwords would ever need to be remembered by the user.
This was the ideal user interaction with First Opinion that we wanted. But that ideal changed as we went along actually building the app. First, as developers, we spend a lot of time logging in and out of the app, and so we kept delaying the email with validation code part of our signup flow because it was not only complex to implement on the backend, but would keep us from easily moving between devices and logging in and out to test the app. Since the validation of users didn't exist, and there were no passwords, during the early days of the app everyone could sign in as everyone else. Obviously, this was going to have to change by the time the app was released into the wild.
As we got closer to launch, we realized how much architecture was really needed to support sending the validation emails. Things like a temporary storage place for those registration codes and an architecture for sending emails. Basically, there needed to be lots of stuff to allow the user to not have to remember a password, and that was on top of the rather large list of other things that still needed to be done before launch, we're a relatively small team (currently three engineers) and we needed to focus our attention on what was absolutely necessary for launch.
So, after a few debates, we decided to compromise between no credentials and a full fledged password, and add a passcode. A passcode feels lighter than a password, and its easier to remember (hopefully!). But most importantly, adding the passcode allowed us to remove all that needed backend work from our plates. What did it take to add the passcode? A slight modification to our user schema and some hashing code to turn the plaintext passcode into a secure hash. Total time spent adding and testing the new passcode was about an hour (at most), I'm guessing doing it the other way would've taken a few days and most likely wouldn't have been as reliable (what happens if the email is delayed? What if it doesn't come at all?).
Arguably, the user lost a bit in this decision, they now have to remember a passcode, but by balancing the needs of the user with the needs of the developer, I think we were able to build an overall better, more reliable product. Most times, in the end, the user doesn't end up with your ideal interface, but hopefully you find the right balance between what you wanted, and what you ship.