Out in the wild we have a users table. A users table is usually
ID | username | salt | encrypted_password | horridly_insecure_reset_key
1 | user1 | foo | 09b6d39aa22fcb8698687e1af09a3af9 | NULL
2 | user2 | bar | 6c07c60f4b02c644ea1037575eb40005 | NULL
3 | user3 | baz | 09b6d39aa22fcb8698687e1af09a3af9 | reset
Then an authentication method will look something like
def authenticate(user, password)
u = User.find(user: user)
return u.encrypted_password == encrypt(password + u.salt)
By having a salt per user it ensures that even if the password for user1 is known you can't figure out the password for user2 or user3 without their salt.
You also ensure that you can't figure out the salt by have a set of encrypted passwords and trying a few encrypted passwords.
In essence, this way, every attack against a user has to be started from scratch.
Even if an attacker has a list of users and salts, they still need to do the cracking against each and every user to see if they have a password match. If you had a pool of salts, or one static salt, I could know that user1's password is password and then just find all the encrypted passwords that match. So this way at least slows them down just a little bit more.
Now when we look at salts, we want to reduce salt reuse. Two identical salts will make it easier to attackers. If two people share the same salt and the same password, breaking one user will break the other.
So lets say that we only use these three salts. And we have 3000 users. that means, 1000 people have the same salt. If 1% of those have a password of "password" then those people can be cracked all at the same time. 10 accounts get hacked at once. Because we know the three salts. That's a very easy stretch to get 30 people attacked at once.
Now if every salt is unique. And we know that user1's password is password, that doesn't do you any good. You still have only cracked 1 user. You still have to do "does password + salt = encrypted password" for all other 2999 users.
A really important note.
Security through obscurity is not security. That doesn't mean you should post your users table on google cause that's silly. But when measuring security, you should assume the attacker has everything. You can't say, "But they won't know the application salt cause they don't have the source code". Cause they could. Doesn't mean give away your salts, it just means it's not real security. Assume they have the user name and the salt, and then try to make it more difficult for them to get the password.
SUPER IMPORTANT NOTE
The code and table used here are about 9,000 times too simple for real use. The password are not encrypted, the salts are too short, the method is a bit to simplistic, in short doing something like this in production is not anything that should be considered secure. I chose these cause there simple for the point of demonstration, not because they are secure.