diff --git a/cmd/root.go b/cmd/root.go
index 4e7fad1e2d9f8ab930c09b7d2382833ce2963931..76934e8282f0b48e11df356bc387e4cbb96ca0ed 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -14,6 +14,8 @@ var rootCmd = &cobra.Command{
 with the cobra library that took inspiration from the old 
 useradm.py. It takes care of LDAP configuration and was 
 made as an update for the previous version.`,
+    // since we already print the errors in Execute()
+    // not having this would print the error twice :p
     SilenceErrors: true,
 }
 
diff --git a/cmd/user.go b/cmd/user.go
index 58d77b1ff3ea4c8e62c6f57db0c44da38f45a674..8358eb268a7da069f032e98ddb593a65590c052e 100644
--- a/cmd/user.go
+++ b/cmd/user.go
@@ -8,6 +8,9 @@ import (
 var userCmd = &cobra.Command{
 	Use:   "user",
 	Short: "User subcommand",
+    Long:  `Subcommand for managing unique users in general.
+If what you are trying to do is create a lot of
+new users, please do so with the bulk subcommand.`,
 }
 
 func init() {
diff --git a/cmd/user/create.go b/cmd/user/create.go
index 4937843b0e230a3aa68d26057c65f10d0939f4f4..94faae8de6f329f03ce2b737d9bebeca2ce7d569 100644
--- a/cmd/user/create.go
+++ b/cmd/user/create.go
@@ -3,8 +3,6 @@ package user
 import (
 	"os"
 	"fmt"
-	"log"
-	"sort"
 	"time"
 	"regexp"
 	"os/exec"
@@ -18,15 +16,33 @@ import (
 )
 
 const (
-    MAIL_ALIAS_FILE = "/etc/aliases"
-    DEF_PERMISSION  = "0700"
-    BLK_PERMISSION  = "0000"
-    HTML_BASE_PERM  = "0755"
-    MAX_VARIANCE    = 50
+    MAIL_ALIAS_FILE = "/etc/aliases" // aliases db path for query
+    DEF_PERMISSION  = "0700"         // default user dir permission
+    BLK_PERMISSION  = "0000"         // user dir permission if blocked
+    HTML_BASE_PERM  = "0755"         // set public_html to this on creation
+    MAX_VARIANCE    = 50             // tries made to create login automatically
     MIN_UID         = 1000
     MAX_UID         = 6000
 )
 
+type userOpts struct {
+    GRR      string
+    GID      string
+    UID      string
+    Resp     string
+    Name     string
+    Nobkp    string
+    Ltype    string
+    Shell    string
+    Passwd   string
+    Webdir   string
+    Status   string
+    Course   string
+    Expiry   string
+    Homedir  string
+    Confirm  bool
+}
+
 var CreateUserCmd = &cobra.Command{
 	Use:   "create",
 	Short: "Create new user",
@@ -34,7 +50,8 @@ var CreateUserCmd = &cobra.Command{
 }
 
 func init() {
-	// Possible Flags
+	// possible flags
+    // FIXME: maybe leave less flags for user input
 	CreateUserCmd.Flags().StringP("grr", "r", "_", "User GRR, required for ini type")
 	CreateUserCmd.Flags().StringP("type", "t", "ini", "Type of auto-generated login: ini, first or last")
 	CreateUserCmd.Flags().StringP("path", "w", "", "Full path to webdir, /home/html/inf/login if empty")
@@ -50,16 +67,17 @@ func init() {
 	CreateUserCmd.Flags().StringP("course", "c", "_", "User course/minicourse, even temp ones")
 	CreateUserCmd.Flags().StringP("nobkp", "b", "", "User nobackup directory path")
 
-	// Required Flags
+	// required flags
 	CreateUserCmd.MarkFlagRequired("name")
 	CreateUserCmd.MarkFlagRequired("group")
+	CreateUserCmd.MarkFlagRequired("course")
 
 	CreateUserCmd.Flags().BoolP("confirm", "y", false, "Skip confirmation prompt")
 }
 
 func createUserFunc(cmd *cobra.Command, args []string) error {
     success := false
-    // Creates model from users input
+    // creates model from users input
     usr, confirm, err := createNewUserModel(cmd)
     if err != nil { return err }
 
@@ -70,12 +88,11 @@ func createUserFunc(cmd *cobra.Command, args []string) error {
         }
     }()
 
-    // Prints info for confirmation
-	//fmt.Printf("%v\n", usr.FullToString())
-	fmt.Printf("%v\n	Passwd:  %v\n\n", usr.ToString(), usr.Password)
+    // prints info for confirmation
+	fmt.Printf("%v\n", usr.FullToString()) // for debug
+	//fmt.Printf("%v\n	Passwd:  %v\n\n", usr.ToString(), usr.Password)
     confirmationPrompt(confirm, "creation")
 
-    // Generate the new password
     if usr.Password == "[auto-generate]" {
         usr.Password = genPassword()
     }
@@ -102,205 +119,121 @@ func createUserFunc(cmd *cobra.Command, args []string) error {
 	return nil
 }
 
+// creates and validates user inputs into the User model
 func createNewUserModel(cmd *cobra.Command) (model.User, bool, error) {
     var u model.User
     users, err := getUsers()
-	if err != nil { return u, false, err }
-	userGRR, err := cmd.Flags().GetString("grr")
-	if err != nil { return u, false, err }
-	userResp, err := cmd.Flags().GetString("resp")
-	if err != nil { return u, false, err }
-	userName, err := cmd.Flags().GetString("name")
-	if err != nil { return u, false, err }
-	userType, err := cmd.Flags().GetString("type")
-	if err != nil { return u, false, err }
-	userGID, err := cmd.Flags().GetString("group")
-	if err != nil { return u, false, err }
-	userUID, err := cmd.Flags().GetString("login")
-	if err != nil { return u, false, err }
-	confirm, err := cmd.Flags().GetBool("confirm")
-	if err != nil { return u, false, err }
-	userShell, err := cmd.Flags().GetString("shell")
-	if err != nil { return u, false, err }
-	userWebdir, err := cmd.Flags().GetString("path")
-	if err != nil { return u, false, err }
-	userStatus, err := cmd.Flags().GetString("status")
-	if err != nil { return u, false, err }
-	userCourse, err := cmd.Flags().GetString("course")
-	if err != nil { return u, false, err }
-	userExpiry, err := cmd.Flags().GetString("expiry")
-	if err != nil { return u, false, err }
-	userHomedir, err := cmd.Flags().GetString("homedir")
-	if err != nil { return u, false, err }
-	userPassword, err := cmd.Flags().GetString("passwd")
-	if err != nil { return u, false, err }
-	userNobackup, err := cmd.Flags().GetString("nobkp")
-	if err != nil { return u, false, err }
+    if err != nil {
+        return u, false, err
+    }
 
     groups, err := getGroups()
-    if err != nil { return u, false, err }
+    if err != nil {
+        return u, false, err
+    }
 
-    err = validateExpiry(userExpiry)
-	if err != nil { return u, false, err }
+    opts, err := retrieveOpts(cmd)
+    if err != nil {
+        return u, false, err
+    }
 
-    err = validateStatus(userStatus)
-	if err != nil { return u, false, err }
+    if err := validateInputs(opts)
+    err != nil {
+	    return u, false, err
+    }
 
-    err = validateGID(groups, userGID)
-	if err != nil { return u, false, err }
+    if opts.Status == "Blocked" {
+        opts.Shell = "/bin/false"
+    }
 
-    err = validateUID(users, userUID)
-	if err != nil { return u, false, err }
+    if opts.Ltype == "ini" && opts.GRR == "_" {
+        return u, false, fmt.Errorf("GRR is required for \"ini\" login type")
+    }
 
-    err = validateGRR(users, userGRR)
-	if err != nil { return u, false, err }
+    if opts.UID == "" {
+        opts.UID, err = genUniqueUID(opts.Name, opts.GRR, opts.Ltype, users)
+        if err != nil {
+            return u, false, err
+        }
+    }
 
-    if userStatus == "Blocked" { userShell = "/bin/false" }
+    opts.Homedir, err = genDirPath("/home", opts.GID, opts.UID, opts.Homedir)
+    if err != nil {
+        return u, false, err
+    }
 
-    if userType == "ini" && userGRR == "_" {
-        err := fmt.Errorf("GRR is required for \"ini\" login type")
-		return u, false, err
+    opts.Nobkp, err = genDirPath("/nobackup", opts.GID, opts.UID, opts.Nobkp)
+    if err != nil {
+        return u, false, err
     }
 
-    if userUID == "" { 
-        used, variance := true, 0
-        for used {
-            userUID = genLogin(userName, userGRR, userType, variance)
-            used = userUID == "" || userUID == "_" ||
-                   loginExists(users, userUID) ||
-                   mailAliasExists(userUID) 
-            variance++
-            if variance > MAX_VARIANCE {
-                log.Fatalf("Could't generate login automatically, please inform the desired login\n")
-            }
-        }
+    opts.Webdir, err = genDirPath("/home/html/inf", "", opts.UID, opts.Webdir)
+    if err != nil {
+        return u, false, err
     }
 
-    userHomedir = genHomedirPath(userUID, userHomedir, userGID)
-    err = validatePath(userHomedir)
-    if err != nil { return u, false, err }
-
-    userNobackup = genNobackupPath(userUID, userGID, userNobackup)
-    err = validatePath(userNobackup)
-    if err != nil { return u, false, err }
-
-    userWebdir = genWebdirPath(userUID, userWebdir)
-    err = validatePath(userWebdir)
-    if err != nil { return u, false, err }
-
-	u = model.User{
-		UID:      userUID,
-		GID:      userGID,
-		GRR:      userGRR,
-        Resp:     userResp,
-		Name:     userName,
-		Ltype:    userType,
-		Shell:    userShell,
-        Status:   userStatus,
-        Expiry:   userExpiry,
-        Course:   userCourse,
-		Webdir:   userWebdir,
-		Homedir:  userHomedir,
-		Nobackup: userNobackup,
-		Password: ifThenElse(userPassword != "", userPassword, "[auto-generate]"),
-	}
+    u = model.User{
+        UID:      opts.UID,
+        GID:      opts.GID,
+        GRR:      opts.GRR,
+        Resp:     opts.Resp,
+        Name:     opts.Name,
+        Ltype:    opts.Ltype,
+        Shell:    opts.Shell,
+        Status:   opts.Status,
+        Expiry:   opts.Expiry,
+        Course:   opts.Course,
+        Webdir:   opts.Webdir,
+        Homedir:  opts.Homedir,
+        Nobackup: opts.Nobkp,
+        Password: ifThenElse(opts.Passwd != "", opts.Passwd, "[auto-generate]"),
+    }
 
     u = genGecos(u)
-    
+
+    // get a new UIDNumber
     newUIDNumber, err := getNewUIDNumber()
     if err != nil {
-        err = fmt.Errorf("Failed to generate new UIDNumber for user: %v", err)
-        return u, false, err
+        return u, false, fmt.Errorf("failed to generate new UIDNumber for user: %v", err)
     }
     u.UIDNumber = newUIDNumber
 
-    for key, value := range groups {
-        if value == u.GID {
+    // assign GIDNumber by traversing the groups
+    for key, val := range groups {
+        if val == u.GID {
             u.GIDNumber = key
+            break
         }
-    } 
+    }
 
     u.DN = "uid=" + u.UID + ",ou=usuarios,dc=c3local"
 
-    return u, confirm, nil
-}
-
-func ifThenElse(condition bool, a string, b string) string {
-	if condition { return a }
-	return b
-}
-
-func genHomedirPath(login string, homedir string, group string) string {
-    if homedir != "" { return homedir }
-    return filepath.Join("/home", group, login)
+    return u, opts.Confirm, nil
 }
 
-func genWebdirPath(login string, webdir string) string {
-    if webdir != "" { return webdir }
-    return filepath.Join("/home/html/inf", login)
+func genDirPath(base, group, login, input string) (string, error) {
+    if input != "" { return input, nil }
+    p := filepath.Join(base, group, login)
+    return p, validatePath(p)
 }
 
-func genNobackupPath(login string, group string, nobkp string) string {
-    if nobkp != "" { return nobkp }
-    return filepath.Join("/nobackup", group, login)
-}
-
-func genGecos(user model.User) model.User {
-    gecos := user.Name + ","
-    gecos += user.GRR + ","
-    gecos += user.Resp + ","
-    gecos += user.Course + ","
-    gecos += user.Status + ","
-    gecos += user.Expiry + ","
-    gecos += user.Ltype + ","
-    gecos += user.Webdir + ","
-    gecos += user.Nobackup
-    user.Gecos = gecos
-    return user
-}
-
-func validatePath(path string) error {
-    _, err := os.Stat(path)
-    if os.IsNotExist(err) { return nil }
-    return fmt.Errorf("Path \"%v\" already exists, please provide a new path", path) 
-}
-
-func validateExpiry(expiry string) error {
-    if expiry == "_" { return nil }
-
-    parts := strings.Split(expiry, ".")
-    if !isValidDate(parts) {
-        err := fmt.Errorf("Malformed expiry date string, use \"dd.mm.yy\"")
-        return err
-    }
-    return nil
-}
-
-func isValidDate(arr []string) bool {
-	if len(arr) != 3 {
-		return false
-	}
-
-	// Convert to int
-	day, err1 := strconv.Atoi(arr[0])
-	month, err2 := strconv.Atoi(arr[1])
-	year, err3 := strconv.Atoi(arr[2])
-
-	if err1 != nil || err2 != nil || err3 != nil {
-		return false
-	}
-
-	// Ensure year is two digits 
-	if year < 0 || year > 99 {
-		return false
-	}
-
-	// Validate the date
-	fullYear := 2000 + year 
-	t := time.Date(fullYear, time.Month(month), day, 0, 0, 0, 0, time.UTC)
-	return t.Day() == day && t.Month() == time.Month(month)
+func genGecos(u model.User) model.User {
+    gecos := u.Name + ","
+    gecos += u.GRR + ","
+    gecos += u.Resp + ","
+    gecos += u.Course + ","
+    gecos += u.Status + ","
+    gecos += u.Expiry + ","
+    gecos += u.Ltype + ","
+    gecos += u.Webdir + ","
+    gecos += u.Nobackup
+    u.Gecos = gecos
+    return u
 }
 
+// basically takes the initials and the grr year.
+// if variance increases we take next letter of
+// 1st name, then next letter of 2nd name, etc...
 func genLoginIni(name string, grr string, variance int) string {
     login := ""
     parts := formatName(name)
@@ -320,11 +253,42 @@ func genLoginIni(name string, grr string, variance int) string {
         }
     }
 
-	login += grr[2:4]
+    return login + grr[2:4]
+}
 
-	return login
+// parent function for generating a login automatically
+func genLogin(name string, grr string, ltype string, variance int) string {
+    var login string
+    switch ltype {
+    case "", "ini":
+        login = genLoginIni(name, grr, variance)
+    case "first":
+        login = genLoginFirst(name, variance)
+    case "last":
+        login = genLoginLast(name, variance)
+    }
+    return login
+}
+
+// removes connectives, leave all lowecase and splits the name
+func formatName(name string) []string {
+    connectives := map[string]bool{"da": true, "de": true, "di": true,
+		            "do": true, "das": true, "dos": true, "von": true}
+	splitName := strings.Fields(name)
+	var parts []string
+
+	for _, part := range splitName {
+		lowerPart := strings.ToLower(part)
+		if !connectives[lowerPart] {
+			parts = append(parts, string(lowerPart))
+		}
+	}
+    return parts
 }
 
+// basically takes 1st name and the initial of the 2nd.
+// if variance increases we take more initials, and if
+// it keeps going we put more letters in each name
 func genLoginFirst(name string, variance int) string {
     parts := formatName(name)
 
@@ -352,6 +316,8 @@ func genLoginFirst(name string, variance int) string {
     return login
 }
 
+// basically takes initials up to last name and then appends it.
+// if variance increases we put more letters in each name
 func genLoginLast(name string, variance int) string {
     login := ""
     parts := formatName(name)
@@ -380,82 +346,24 @@ func genLoginLast(name string, variance int) string {
     return login
 }
 
-func genLogin(name string, grr string, ltype string, variance int) string {
-    var login string
-    switch ltype {
-    case "", "ini":
-        login = genLoginIni(name, grr, variance)
-    case "first":
-        login = genLoginFirst(name, variance)
-    case "last":
-        login = genLoginLast(name, variance)
-    }
-    return login
-}
-
-func validateStatus(status string) error {
-    if status != "Blocked" && status != "Active" {
-        err := fmt.Errorf("User status can only be \"Active\" or \"Blocked\"")
-        return err
-    }
-    return nil
-}
-
-func validateGRR(users []model.User, grr string) error {
-    if grr == "_" { return nil }
-
-    isValid, _ := regexp.MatchString(`^\d{8}$`, grr) // is 8 digit number
-	if !isValid {
-        err := fmt.Errorf("Malformed GRR string, must be 8 digit number")
-		return err
-	}
-
-    if grrExists(users, grr) {
-        err := fmt.Errorf(`The informed GRR already exists in LDAP database
-Note: To search for the account use "useradm user show -r %s"`, grr)
-        return err
-    }
+func genUniqueUID(name, grr, ltype string, users []model.User) (string, error) {
+    var uid string
+    used, variance := true, 0
+    for used {
+        uid = genLogin(name, grr, ltype, variance)
 
-    return nil
-}
-
-func validateGID(groups map[string]string, group string) error {
-    for _, value := range groups {
-        if value == group {
-            return nil
-        }
-    }
-    err := fmt.Errorf("Could't find group \"%v\" in LDAP database", group)
-    return err
-}
+        // already taken or alias for it exists :(
+        used = loginExists(users, uid) || mailAliasExists(uid)
 
-func validateUID(users []model.User, login string) error {
-    for i := range users {
-        if users[i].UID == login {
-            err := fmt.Errorf(`The informed Login already exists in LDAP database
-Note: To search for the account use "useradm user show -l %s"`, login)
-            return err
+        variance++
+        if variance > MAX_VARIANCE {
+            return "", fmt.Errorf("Could't generate login automatically, please inform the desired login\n")
         }
     }
-    return nil
-}
-
-func formatName(name string) []string {
-    connectives := map[string]bool{"da": true, "de": true, "di": true, 
-		            "do": true, "das": true, "dos": true, "von": true}
-	splitName := strings.Fields(name)
-	var parts []string
-
-	for _, part := range splitName {
-		lowerPart := strings.ToLower(part)
-		if !connectives[lowerPart] {
-			parts = append(parts, string(lowerPart))
-		}
-	}
-    return parts
+    return uid, nil
 }
 
-// Queries to check if the alias exists
+// queries to check if the alias exists
 func mailAliasExists(alias string) bool {
     cmd := exec.Command("/usr/sbin/postalias", "-q", alias, MAIL_ALIAS_FILE)
     cmd.Stdout = nil
@@ -463,7 +371,8 @@ func mailAliasExists(alias string) bool {
     return cmd.Run() == nil
 } 
 
-func getNewUIDNumberLinear() (string, error) {
+// finds next available uidNumber (MEX from uid group)
+func getNewUIDNumber() (string, error) {
     uids, err := getUIDs()
     if err != nil {
         return "", err
@@ -471,9 +380,9 @@ func getNewUIDNumberLinear() (string, error) {
 
     candidate := MIN_UID
     for _, uid := range uids {
-        if uid == candidate { // Check if taken
+        if uid == candidate { // check if taken
             candidate++
-        } else if uid > candidate { // Found a gap
+        } else if uid > candidate { // found a gap
             break
         }
     }
@@ -484,24 +393,7 @@ func getNewUIDNumberLinear() (string, error) {
     return strconv.Itoa(candidate), nil
 }
 
-// Finds next available uidNumber
-func getNewUIDNumber() (string, error) {
-    uids, err := getUIDs()
-    if err != nil {
-        return "", err
-    }
-    for candidate := MIN_UID; candidate <= MAX_UID; candidate++ {
-        i := sort.Search(len(uids), func(j int) bool {
-            return uids[j] >= candidate
-        })
-        if i == len(uids) || uids[i] != candidate {
-            // Candidate UID is not in the list, so this is available.
-            return strconv.Itoa(candidate), nil
-        }
-    }
-    return "", fmt.Errorf("no more available UID numbers")
-}
-
+// generates a LDAP request and adds the user to LDAP
 func addUserLDAP(u model.User) error {
     l, err := connLDAP()
     if err != nil {
@@ -527,6 +419,7 @@ func addUserLDAP(u model.User) error {
     return nil
 }
 
+// creates a KerberosPrincipal for the user
 func addKerberosPrincipal(login string) error {
 	cmd := exec.Command("kadmin.local", "-q",
 		fmt.Sprintf("addprinc -policy padrao -randkey -x dn=uid=%s,ou=usuarios,dc=c3local %s", login, login))
@@ -539,6 +432,7 @@ func addKerberosPrincipal(login string) error {
 	return nil
 }
 
+// sets the password for the user (via kerberos)
 func modKerberosPassword(login, password string) error {
 	cmd := exec.Command("kadmin.local", "-q",
 		fmt.Sprintf("cpw -pw %s %s", password, login))
@@ -555,6 +449,7 @@ func modKerberosPassword(login, password string) error {
 
 func createUserDirs(u model.User) error {
     success := false
+
     defer func() {
         if !success {
             fmt.Println("Error found creating dirs, cleaning up...")
@@ -584,7 +479,7 @@ func createHome(u model.User, homeDir string) error {
         perm = BLK_PERMISSION
     }
 
-	// Create directory
+	// create directory
 	cmd := exec.Command("mkdir", "-p", homeDir)
     cmd.Stdout = nil
     cmd.Stderr = nil
@@ -592,19 +487,19 @@ func createHome(u model.User, homeDir string) error {
 		return fmt.Errorf("Failed to create home directory: %w", err)
 	}
 
-	// Copy /etc/skel
+	// copy /etc/skel
 	cmd = exec.Command("cp", "-r", "/etc/skel/.", homeDir)
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("Failed to copy /etc/skel contents: %w", err)
 	}
 
-	// Change permissions
+	// change permissions
     cmd = exec.Command("chmod", perm, homeDir)
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("Failed to set permissions: %w", err)
 	}
 
-	// Change ownership
+	// change ownership
     cmd = exec.Command("chown", "-R", fmt.Sprintf("%s:%s", u.UID, u.GID), homeDir)
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("Failed to change ownership: %w", err)
@@ -627,7 +522,7 @@ func createWeb(u model.User) error {
         }
     }()
 
-	// Create directory
+	// create directory
 	cmd := exec.Command("mkdir", "-p", u.Webdir)
     cmd.Stdout = nil
     cmd.Stderr = nil
@@ -635,30 +530,31 @@ func createWeb(u model.User) error {
 		return fmt.Errorf("Failed to create web directory: %w", err)
 	}
 
-	// Create index
+	// create index
 	cmd = exec.Command("touch", filepath.Join(u.Webdir, "index.html"))
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("Failed to create index.html: %w", err)
 	}
 
-    // Create link in users home
+    // create link in users home
 	cmd = exec.Command("ln", "-s", u.Webdir, filepath.Join(u.Homedir, "public_html"))
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("Failed to create link public_html: %w", err)
 	}
 
-	// Change permissions
+	// change permissions
     cmd = exec.Command("chmod", perm, u.Webdir)
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("Failed to set permissions: %w", err)
 	}
 
-	// Change ownership
+	// change ownership
     cmd = exec.Command("chown", "-R", fmt.Sprintf("%s:%s", u.UID, u.GID), u.Webdir)
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("Failed to change ownership: %w", err)
 	}
 
+    // set permission for public_html
 	cmd = exec.Command("chmod", HTML_BASE_PERM, filepath.Join(u.Homedir, "public_html"))
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("Failed to create set permissions for public_html: %w", err)
@@ -667,3 +563,190 @@ func createWeb(u model.User) error {
     success = true
     return nil
 }
+
+func validatePath(path string) error {
+    _, err := os.Stat(path)
+    if os.IsNotExist(err) { return nil }
+    return fmt.Errorf("Path \"%v\" already exists, please provide a new path", path)
+}
+
+func validateExpiry(expiry string) error {
+    if expiry == "_" { return nil }
+
+    parts := strings.Split(expiry, ".")
+    if !isValidDate(parts) {
+        err := fmt.Errorf("Malformed expiry date string, use \"dd.mm.yy\"")
+        return err
+    }
+    return nil
+}
+
+func validateStatus(status string) error {
+    if status != "Blocked" && status != "Active" {
+        err := fmt.Errorf("User status can only be \"Active\" or \"Blocked\"")
+        return err
+    }
+    return nil
+}
+
+func validateGRR(grr string) error {
+    // OK if empty, only "ini" login type requires it and we check :)
+    if grr == "_" { return nil }
+
+    users, err := getUsers()
+    if err != nil { return err }
+
+    isValid, _ := regexp.MatchString(`^\d{8}$`, grr) // is 8 digit number
+	if !isValid {
+        err := fmt.Errorf("Malformed GRR string, must be 8 digit number")
+		return err
+	}
+
+    if grrExists(users, grr) {
+        err := fmt.Errorf(`The informed GRR already exists in LDAP database
+Note: To search for the account use "useradm user show -r %s"`, grr)
+        return err
+    }
+
+    return nil
+}
+
+func validateGID(group string) error {
+    var err error
+
+    groups, err := getGroups()
+    if err != nil { return err }
+
+    for _, value := range groups {
+        if value == group {
+            return nil
+        }
+    }
+    err = fmt.Errorf("Could't find group \"%v\" in LDAP database", group)
+    return err
+}
+
+func validateUID(login string) error {
+    users, err := getUsers()
+    if err != nil { return err }
+    res := searchUser(users, false, login, "", "", "", "", "")
+    if len(res) != 0 {
+        return fmt.Errorf(`The informed Login already exists in LDAP database
+Note: To search for the account use "useradm user show -l %s"`, login)
+    }
+    return nil
+}
+
+func validateInputs(opts userOpts) error {
+    var err error
+
+    err = validateGID(opts.GID)
+    if err != nil { return err }
+
+    err = validateGRR(opts.GRR)
+    if err != nil { return err }
+
+    err = validateExpiry(opts.Expiry)
+    if err != nil { return err }
+
+    err = validateStatus(opts.Status)
+    if err != nil { return err }
+
+    // it’s OK if UID is empty here, we generate it later :)
+    if opts.UID != "" {
+        err := validateUID(opts.UID)
+        if err != nil { return err }
+    }
+
+    return nil
+}
+
+func getFlagString(cmd *cobra.Command, flag string) (string, error) {
+    flagValue, err := cmd.Flags().GetString(flag)
+    if err != nil {
+        return "", fmt.Errorf("Failed to get flag %q: %w", flag, err)
+    }
+    return flagValue, nil
+}
+
+func retrieveOpts(cmd *cobra.Command) (userOpts, error) {
+    var opts userOpts
+    var err error
+
+    opts.GRR, err = getFlagString(cmd, "grr")
+    if err != nil { return opts, err }
+
+    opts.Resp, err = getFlagString(cmd, "resp")
+    if err != nil { return opts, err }
+
+    opts.Name, err = getFlagString(cmd, "name")
+    if err != nil { return opts, err }
+
+    opts.GID, err = getFlagString(cmd, "group")
+    if err != nil { return opts, err }
+
+    opts.UID, err = getFlagString(cmd, "login")
+    if err != nil { return opts, err }
+
+    opts.Ltype, err = getFlagString(cmd, "type")
+    if err != nil { return opts, err }
+
+    opts.Shell, err = getFlagString(cmd, "shell")
+    if err != nil { return opts, err }
+
+    opts.Webdir, err = getFlagString(cmd, "path")
+    if err != nil { return opts, err }
+
+    opts.Nobkp, err = getFlagString(cmd, "nobkp")
+    if err != nil { return opts, err }
+
+    opts.Status, err = getFlagString(cmd, "status")
+    if err != nil { return opts, err }
+
+    opts.Course, err = getFlagString(cmd, "course")
+    if err != nil { return opts, err }
+
+    opts.Expiry, err = getFlagString(cmd, "expiry")
+    if err != nil { return opts, err }
+
+    opts.Passwd, err = getFlagString(cmd, "passwd")
+    if err != nil { return opts, err }
+
+    opts.Homedir, err = getFlagString(cmd, "homedir")
+    if err != nil { return opts, err }
+
+    opts.Confirm, err = cmd.Flags().GetBool("confirm")
+    if err != nil { return opts, err }
+
+    return opts, nil
+}
+
+func isValidDate(arr []string) bool {
+	if len(arr) != 3 {
+		return false
+	}
+
+	// convert to int
+	day,  err1 := strconv.Atoi(arr[0])
+	mth,  err2 := strconv.Atoi(arr[1])
+	year, err3 := strconv.Atoi(arr[2])
+
+	if err1 != nil || err2 != nil || err3 != nil {
+		return false
+	}
+
+	// ensure year is two digits
+	if year < 0 || year > 99 {
+		return false
+	}
+
+	// validate the date
+	fullYear := 2000 + year
+	t := time.Date(fullYear, time.Month(mth), day, 0, 0, 0, 0, time.UTC)
+	return t.Day() == day && t.Month() == time.Month(mth)
+}
+
+func ifThenElse(condition bool, a string, b string) string {
+	if condition { return a }
+	return b
+}
diff --git a/cmd/user/delete.go b/cmd/user/delete.go
index bdc5c6d308cb20480a4f4dd8d97f1af696a5e6de..115af95ed6d00cb70b8859b91c06d52d1f4ef051 100644
--- a/cmd/user/delete.go
+++ b/cmd/user/delete.go
@@ -37,8 +37,10 @@ func init() {
 
 func deleteUserFunc(cmd *cobra.Command, args []string) error {
     success := false
-    userLogin, err := cmd.Flags().GetString("login")
+
+    userLogin, err := getFlagString(cmd, "login")
     if err != nil { return err }
+
     confirm, err := cmd.Flags().GetBool("confirm")
     if err != nil { return err }
 
@@ -68,25 +70,31 @@ func deleteUserFunc(cmd *cobra.Command, args []string) error {
     if err != nil { return err }
 
     fmt.Printf("\nUser removed!\n")
+
     success = true
     return nil
 }
 
+// searches for the specified login string, there can only be one >:)
 func locateUser(login string) (model.User, error) {
     var u model.User
     users, err := getUsers()
+
     if !loginExists(users, login) {
         return u, fmt.Errorf("Failed to find login in LDAP database: %v", err)
     }
+
     filter := searchUser(users, false, login, "", "", "", "", "")
     if len(filter) != 1 {
         return u, fmt.Errorf(`More than one user matched the login given.
 search made: "useradm user show -l %v"`, login)
     }
+
     u = filter[0]
     return u, nil
 }
 
+// moves dirs to their respective trash dir
 func removeDirs(u model.User) error {
     err := moveAndChown(u.Homedir, HOME_TRASH, "nobody", "nogroup")
     if err != nil { return err }
@@ -107,21 +115,23 @@ func delUserLDAP(u model.User) error {
     }
     defer l.Close()
 
-    // Remove user from all groups 
+    // first remove user from all groups
     searchReq := ldap.NewSearchRequest(
         "ou=grupos,dc=c3local,dc=com", 
         ldap.ScopeWholeSubtree, 
         ldap.NeverDerefAliases, 0, 0, false,
-        "(memberUid=" + u.UID + ")", // Filter by UID membership
+        "(memberUid=" + u.UID + ")", // filter by UID membership
         []string{"dn"}, 
         nil,
     )
     
+    // searches groups for target
     groups, err := l.Search(searchReq)
     if err != nil && !ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
         return fmt.Errorf("Group members search failed: %v", err)
     }
 
+    // removing from groups
     for _, entry := range groups.Entries {
         modReq := ldap.NewModifyRequest(entry.DN, nil)
         modReq.Delete("memberUid", []string{u.UID})
@@ -131,7 +141,7 @@ func delUserLDAP(u model.User) error {
         }
     }
 
-    // Delete user entry
+    // removing user entry
     delReq := ldap.NewDelRequest(u.DN, nil)
     if err := l.Del(delReq); err != nil && 
               !ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
@@ -141,6 +151,7 @@ func delUserLDAP(u model.User) error {
     return nil
 }
 
+// usually not necessary but used in a failed create command
 func delKerberosPrincipal(login string) error {
     cmd := exec.Command("kadmin.local", "-q", fmt.Sprintf("delprinc -force %s", login))
 
@@ -153,27 +164,27 @@ func delKerberosPrincipal(login string) error {
 }
 
 func moveAndChown(orig, dest, owner, group string) error {
-    // Check if orig exists
+    // check if orig exists
     if _, err := os.Stat(orig); err != nil {
         log.Printf("Directory %v not found so not moved\n", orig)
         return nil
     }
 
-	// Construct destination path
+	// construct destination path
 	destPath := filepath.Join(dest, filepath.Base(orig))
 
     if _, err := os.Stat(destPath); err == nil {
-        return fmt.Errorf("Directory %v already exists\n", destPath)
+        return fmt.Errorf("Directory %v already exists, can't move\n", destPath)
     }
 
-	// Move directory using shell command
+	// move directory
 	cmd := exec.Command("mv", orig, destPath)
 	if output, err := cmd.CombinedOutput(); err != nil {
 		return fmt.Errorf("Failed to move dirs: %w\nOutput: %v", err, output)
 	}
 
-	// Recursive chown using shell command
-	cmd = exec.Command("chown", "-R", owner+":"+group, destPath)
+	// recursive chown
+    cmd = exec.Command("chown", "-R", owner+":"+group, destPath)
 	if output, err := cmd.CombinedOutput(); err != nil {
 		return fmt.Errorf("Failed to set owner/group: %w\nOutput: %v", err, output)
 	}
diff --git a/cmd/user/mod.go b/cmd/user/mod.go
index 632d471760ef3f41eaec49fcf96fcfccc16d7da7..856ed39641477f00930aa3bb3d937929948613bc 100644
--- a/cmd/user/mod.go
+++ b/cmd/user/mod.go
@@ -19,6 +19,7 @@ var ModCmd = &cobra.Command{
 }
 
 func init() {
+    // possible flags
     ModCmd.Flags().StringP("grr", "r", "", "New user GRR")
     ModCmd.Flags().StringP("shell", "s", "", "New user shell")
     ModCmd.Flags().StringP("login", "l", "", "User login name")
@@ -30,6 +31,7 @@ func init() {
     ModCmd.Flags().StringP("password", "p", "", "New password in plain text (use \"auto\" to autogenerate)")
     ModCmd.Flags().BoolP  ("block", "b", false, "Block the user (changes the password and sets their shell to /bin/false)")
 
+    // required flags
     ModCmd.MarkFlagRequired("login")
 
     ModCmd.Flags().BoolP("confirm", "y", false, "Skip confirmation prompt")
@@ -129,6 +131,7 @@ func modifyUserFunc(cmd *cobra.Command, args []string) error {
     return nil
 }
 
+// do NOT include ':' in the charset, it WILL break the command
 func genPassword() string {
     const charset = "@*()=+[];,.?123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
     b := make([]byte, 20)
@@ -141,11 +144,14 @@ func genPassword() string {
     return string(b)
 }
 
+// prints a confirmation prompt, given the operation being performed
 func confirmationPrompt(confirm bool, operation string) {
     if !confirm {
         fmt.Printf("Proceed with user %v? [y/N] ", operation)
+
         reader := bufio.NewReader(os.Stdin)
         response, _ := reader.ReadString('\n')
+
         if strings.TrimSpace(strings.ToLower(response)) != "y" {
             fmt.Fprintln(os.Stderr, "Aborted.")
             os.Exit(1)
diff --git a/cmd/user/show.go b/cmd/user/show.go
index dd3fb5549eba734ff560d9b405559c3ed55aca49..277a74a2a98bab5c5a96e214ff54ee880f30b2a1 100644
--- a/cmd/user/show.go
+++ b/cmd/user/show.go
@@ -13,7 +13,7 @@ import (
 )
 
 const (
-    NUM_GECOS_FIELDS = 9
+    NUM_GECOS_FIELDS = 9    // change if going to add new field
     PASSWD_PATH = "/etc/ldapscripts/ldapscripts.passwd"
 )
 
@@ -24,7 +24,7 @@ var ShowCmd = &cobra.Command{
 }
 
 func init() {
-    // Possible search fields
+    // possible search fields
     ShowCmd.Flags().StringP("grr", "r", "", "Search by user GRR")
     ShowCmd.Flags().StringP("name", "n", "", "Search by user name (case sensitive)")
     ShowCmd.Flags().StringP("login", "l", "", "Search by login")
@@ -32,27 +32,34 @@ func init() {
     ShowCmd.Flags().StringP("status", "s", "", "Search by user status (Active/Blocked)")
     ShowCmd.Flags().StringP("homedir", "d", "", "Search by user homedir")
 
-    ShowCmd.Flags().BoolP("ignore", "i", false, "Make the search case-insensitive")
-
-    // At least one is required!
+    // at least one is required!
     ShowCmd.MarkFlagsOneRequired("grr", "name", "login", "group", "homedir", "status")
+
+    ShowCmd.Flags().BoolP("ignore", "i", false, "Make the search case-insensitive")
 }
 
 func searchUserFunc(cmd *cobra.Command, args []string) error {
-    grr, err := cmd.Flags().GetString("grr")
+    grr, err := getFlagString(cmd,"grr")
     if err != nil { return err }
-    name, err := cmd.Flags().GetString("name")
+
+    name, err := getFlagString(cmd,"name")
     if err != nil { return err }
-    login, err := cmd.Flags().GetString("login")
+
+    login, err := getFlagString(cmd,"login")
     if err != nil { return err }
-    group, err := cmd.Flags().GetString("group")
+
+    group, err := getFlagString(cmd,"group")
     if err != nil { return err }
-    homedir, err := cmd.Flags().GetString("homedir")
+
+    homedir, err := getFlagString(cmd,"homedir")
     if err != nil { return err }
-    status, err := cmd.Flags().GetString("status")
+
+    status, err := getFlagString(cmd,"status")
     if err != nil { return err }
+
     ig, err := cmd.Flags().GetBool("ignore")
     if err != nil { return err }
+
     users, err := getUsers()
     if err != nil { return err }
 
@@ -75,7 +82,7 @@ func searchUser(users []model.User, ig bool, l, g, n, r, s, h string) []model.Us
                     strings.ToLower(substr),
                 )
             }
-            return strings.Contains(src, substr)
+            return strings.Contains(src, substr) // normal search
         }
 
         // Search
@@ -88,7 +95,7 @@ func searchUser(users []model.User, ig bool, l, g, n, r, s, h string) []model.Us
     })
 }
 
-// Generic function for filtering data types, in this case the model User
+// generic function for filtering data types, in this case the model User
 func Filter[T any](slice []T, predicate func(T) bool) []T {
     var result []T
     for _, v := range slice {
@@ -100,17 +107,15 @@ func Filter[T any](slice []T, predicate func(T) bool) []T {
 }
 
 func getUsers() ([]model.User, error) {
-
     var users []model.User
 
-    // Connect
     l, err := connLDAP() 
     if err != nil {
         return users, err
     }
     defer l.Close()
 
-    // Create the LDAP search request
+    // create the LDAP search request
     searchRequest := ldap.NewSearchRequest(
         "dc=c3local",                // search base
         ldap.ScopeWholeSubtree,      // scope
@@ -124,21 +129,21 @@ func getUsers() ([]model.User, error) {
         nil,
     )
 
-    // Perform the search
+    // perform the search
     sr, err := l.Search(searchRequest)
     if err != nil {
         err = fmt.Errorf("Failed to fetch users from LDAP: %v", err)
         return nil, err
     }
 
-    // Get all the groups and ids
+    // get all the groups and ids
     groups, err := getGroups()
     if err != nil {
         err = fmt.Errorf("Failed to fetch groups and gids from LDAP: %v", err) 
         return nil, err
     }
 
-    // Iterate over the search results
+    // iterate over the search results
     for _, entry := range sr.Entries {
         shell := entry.GetAttributeValue("loginShell")
 
@@ -158,7 +163,7 @@ func getUsers() ([]model.User, error) {
 
         gidNumber := user.GIDNumber
 
-        // Safe assignment
+        // safe assignment :)
         groupName, exists := groups[gidNumber]
         if !exists {
             fmt.Printf("WARNING: no group found for GIDNumber %s, user %s. Continuing...", gidNumber, user.UID)
@@ -174,21 +179,21 @@ func getUsers() ([]model.User, error) {
 }
 
 func connLDAP() (*ldap.Conn, error) {
-    // Connect to the LDAP server
+    // connect to the LDAP server
     l, err := ldap.DialURL("ldapi:///")
     if err != nil {
         err = fmt.Errorf("Failed to connect to LDAP: %v", err)
         return nil, err
     }
 
-    // Get admin credentials
+    // get admin credentials
     password, err := getLDAPPassword(PASSWD_PATH)
     if err != nil {
         err = fmt.Errorf("Failed to read LDAP password: %v", err)
         return nil, err
     }
 
-    // Bind using admin credentials
+    // bind using admin credentials
     err = l.Bind("cn=admin,dc=c3local", password)
     if err != nil {
         l.Close()
@@ -211,16 +216,16 @@ func getLDAPPassword(path string) (string, error) {
     return password, nil
 }
 
-// Campos do GECOS:
-// 0. Nome completo do titular da conta
-// 1. GRR do aluno
-// 2. Professor responsavel
-// 3. Curso/minicurso do usuario (ate para temporarios) 
-// 4. Validade da conta (data maxima para delecao)
-// 5. Status da conta (Blocked/Active)
-// 6. Tipo de login (ini, first, last)
-// 7. Webdir do usuario
-// 8. Nobackup do usuario
+// Gecos Fields:
+// 0. users full name
+// 1. users grr
+// 2. responsible professor
+// 3. users course/minicourse (even temp)
+// 4. account expiry date (may be used for deletion)
+// 5. account status (Blocked/Active)
+// 6. login type (ini, first or last)
+// 7. users webdir
+// 8. users nobackup dir
 // 9. to be continued...
 func parseGecos(user model.User) model.User {
     result := [NUM_GECOS_FIELDS]string{0: "_"}
@@ -238,10 +243,11 @@ func parseGecos(user model.User) model.User {
     user.GRR    = result[1]
     user.Resp   = result[2]
     user.Course = result[3]
+    user.Expiry = result[4]
+    // only set with gecos if empty
     if user.Status == "_"{
         user.Status = result[5]
     }
-    user.Expiry = result[4]
     user.Ltype  = result[6]
     user.Webdir = result[7]
     user.Nobackup = result[8]
@@ -249,9 +255,7 @@ func parseGecos(user model.User) model.User {
     return user
 }
 
-
 func getGroups() (map[string]string, error) {
-
     groupMap := make(map[string]string)
 
     l, err := connLDAP() 
@@ -260,22 +264,24 @@ func getGroups() (map[string]string, error) {
     }
     defer l.Close()
 
+    // build search
     searchRequest := ldap.NewSearchRequest(
         "dc=c3local",           
         ldap.ScopeWholeSubtree, 
         ldap.NeverDerefAliases, 
-        0, 0,                      
-        false,                  
+        0, 0, false,
         "(&(objectClass=posixGroup))", 
         []string{"gidNumber", "cn"},   
         nil,
     )
 
+    // search
     sr, err := l.Search(searchRequest)
     if err != nil {
         return nil, fmt.Errorf("LDAP search failed: %w", err)
     }
 
+    // arrange result into a map string->string
     for _, entry := range sr.Entries {
         gid := entry.GetAttributeValue("gidNumber")
         cn := entry.GetAttributeValue("cn")
@@ -288,7 +294,6 @@ func getGroups() (map[string]string, error) {
 }
 
 func getUIDs() ([]int, error) {
-
     var uidNumbers []int
 
     l, err := connLDAP() 
@@ -297,22 +302,24 @@ func getUIDs() ([]int, error) {
     }
     defer l.Close()
 
+    // build search
     searchRequest := ldap.NewSearchRequest(
         "dc=c3local",
         ldap.ScopeWholeSubtree,
         ldap.NeverDerefAliases,
-        0, 0,
-        false,
+        0, 0, false,
         "(&(objectClass=posixAccount))",
         []string{"uidNumber"},
         nil,
     )
 
+    // search
     sr, err := l.Search(searchRequest)
     if err != nil {
         return nil, fmt.Errorf("LDAP search failed: %w", err)
     }
 
+    // arrange result into a array of integers
     for _, entry := range sr.Entries {
         uidStr := entry.GetAttributeValue("uidNumber")
         if uidStr != "" {
@@ -324,6 +331,7 @@ func getUIDs() ([]int, error) {
         }
     }
 
+    // sort for ease of use :)
     sort.Ints(uidNumbers)
 
     return uidNumbers, nil
@@ -333,11 +341,13 @@ func loginExists(users []model.User, login string) bool {
     return exists(users, func(u model.User) bool { return u.UID == login })
 }
 
+// accepts GRRs in the format "GRRXXXXXXXX" aswell as 8 digit number
 func grrExists(users []model.User, grr string) bool {
     return exists(users, func(u model.User) bool { return u.GRR == grr }) ||
            exists(users, func(u model.User) bool { return u.GRR == "GRR" + grr }) 
 }
 
+// generic function for check existence of data types
 func exists[T any](slice []T, predicate func(T) bool) bool {
     for _, item := range slice {
         if predicate(item) {
diff --git a/model/user.go b/model/user.go
index 901c8a43e909e75d53e8b1bd22114ba38a5defe0..2456d6e6a88ae3c03e211ed714130fdeb1477120 100644
--- a/model/user.go
+++ b/model/user.go
@@ -41,6 +41,7 @@ func (u *User) ToString() string {
 		u.Nobackup, u.Status, u.Resp, u.Course, u.Expiry)
 }
 
+// useful for debugging :)
 func (u *User) FullToString() string {
     return fmt.Sprintf(`User:
 	DN:        %s