diff --git a/pkg/commands/cache.go b/pkg/commands/cache.go
index 904c2a1f..f95fdfae 100644
--- a/pkg/commands/cache.go
+++ b/pkg/commands/cache.go
@@ -8,7 +8,6 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/golangci/golangci-lint/internal/cache"
-	"github.com/golangci/golangci-lint/pkg/exitcodes"
 	"github.com/golangci/golangci-lint/pkg/fsutils"
 	"github.com/golangci/golangci-lint/pkg/logutils"
 )
@@ -17,57 +16,48 @@ func (e *Executor) initCache() {
 	cacheCmd := &cobra.Command{
 		Use:   "cache",
 		Short: "Cache control and information",
-		Run: func(cmd *cobra.Command, args []string) {
-			if len(args) != 0 {
-				e.log.Fatalf("Usage: golangci-lint cache")
-			}
-			if err := cmd.Help(); err != nil {
-				e.log.Fatalf("Can't run cache: %s", err)
-			}
+		Args:  cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, _ []string) error {
+			return cmd.Help()
 		},
 	}
 	e.rootCmd.AddCommand(cacheCmd)
 
 	cacheCmd.AddCommand(&cobra.Command{
-		Use:   "clean",
-		Short: "Clean cache",
-		Run:   e.executeCleanCache,
+		Use:               "clean",
+		Short:             "Clean cache",
+		Args:              cobra.NoArgs,
+		ValidArgsFunction: cobra.NoFileCompletions,
+		RunE:              e.executeCleanCache,
 	})
 	cacheCmd.AddCommand(&cobra.Command{
-		Use:   "status",
-		Short: "Show cache status",
-		Run:   e.executeCacheStatus,
+		Use:               "status",
+		Short:             "Show cache status",
+		Args:              cobra.NoArgs,
+		ValidArgsFunction: cobra.NoFileCompletions,
+		Run:               e.executeCacheStatus,
 	})
 
 	// TODO: add trim command?
 }
 
-func (e *Executor) executeCleanCache(_ *cobra.Command, args []string) {
-	if len(args) != 0 {
-		e.log.Fatalf("Usage: golangci-lint cache clean")
-	}
-
+func (e *Executor) executeCleanCache(_ *cobra.Command, _ []string) error {
 	cacheDir := cache.DefaultDir()
 	if err := os.RemoveAll(cacheDir); err != nil {
-		e.log.Fatalf("Failed to remove dir %s: %s", cacheDir, err)
+		return fmt.Errorf("failed to remove dir %s: %w", cacheDir, err)
 	}
 
-	os.Exit(exitcodes.Success)
+	return nil
 }
 
-func (e *Executor) executeCacheStatus(_ *cobra.Command, args []string) {
-	if len(args) != 0 {
-		e.log.Fatalf("Usage: golangci-lint cache status")
-	}
-
+func (e *Executor) executeCacheStatus(_ *cobra.Command, _ []string) {
 	cacheDir := cache.DefaultDir()
 	fmt.Fprintf(logutils.StdOut, "Dir: %s\n", cacheDir)
+
 	cacheSizeBytes, err := dirSizeBytes(cacheDir)
 	if err == nil {
 		fmt.Fprintf(logutils.StdOut, "Size: %s\n", fsutils.PrettifyBytesCount(cacheSizeBytes))
 	}
-
-	os.Exit(exitcodes.Success)
 }
 
 func dirSizeBytes(path string) (int64, error) {
diff --git a/pkg/commands/config.go b/pkg/commands/config.go
index e9546d32..a16ef631 100644
--- a/pkg/commands/config.go
+++ b/pkg/commands/config.go
@@ -15,21 +15,19 @@ func (e *Executor) initConfig() {
 	cmd := &cobra.Command{
 		Use:   "config",
 		Short: "Config",
-		Run: func(cmd *cobra.Command, args []string) {
-			if len(args) != 0 {
-				e.log.Fatalf("Usage: golangci-lint config")
-			}
-			if err := cmd.Help(); err != nil {
-				e.log.Fatalf("Can't run help: %s", err)
-			}
+		Args:  cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, _ []string) error {
+			return cmd.Help()
 		},
 	}
 	e.rootCmd.AddCommand(cmd)
 
 	pathCmd := &cobra.Command{
-		Use:   "path",
-		Short: "Print used config path",
-		Run:   e.executePathCmd,
+		Use:               "path",
+		Short:             "Print used config path",
+		Args:              cobra.NoArgs,
+		ValidArgsFunction: cobra.NoFileCompletions,
+		Run:               e.executePathCmd,
 	}
 	e.initRunConfiguration(pathCmd) // allow --config
 	cmd.AddCommand(pathCmd)
@@ -52,11 +50,7 @@ func (e *Executor) getUsedConfig() string {
 	return prettyUsedConfigFile
 }
 
-func (e *Executor) executePathCmd(_ *cobra.Command, args []string) {
-	if len(args) != 0 {
-		e.log.Fatalf("Usage: golangci-lint config path")
-	}
-
+func (e *Executor) executePathCmd(_ *cobra.Command, _ []string) {
 	usedConfigFile := e.getUsedConfig()
 	if usedConfigFile == "" {
 		e.log.Warnf("No config file detected")
@@ -64,5 +58,4 @@ func (e *Executor) executePathCmd(_ *cobra.Command, args []string) {
 	}
 
 	fmt.Println(usedConfigFile)
-	os.Exit(exitcodes.Success)
 }
diff --git a/pkg/commands/help.go b/pkg/commands/help.go
index a6ed4c84..61d362e7 100644
--- a/pkg/commands/help.go
+++ b/pkg/commands/help.go
@@ -2,14 +2,12 @@ package commands
 
 import (
 	"fmt"
-	"os"
 	"sort"
 	"strings"
 
 	"github.com/fatih/color"
 	"github.com/spf13/cobra"
 
-	"github.com/golangci/golangci-lint/pkg/exitcodes"
 	"github.com/golangci/golangci-lint/pkg/lint/linter"
 	"github.com/golangci/golangci-lint/pkg/logutils"
 )
@@ -18,21 +16,19 @@ func (e *Executor) initHelp() {
 	helpCmd := &cobra.Command{
 		Use:   "help",
 		Short: "Help",
-		Run: func(cmd *cobra.Command, args []string) {
-			if len(args) != 0 {
-				e.log.Fatalf("Usage: golangci-lint help")
-			}
-			if err := cmd.Help(); err != nil {
-				e.log.Fatalf("Can't run help: %s", err)
-			}
+		Args:  cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, _ []string) error {
+			return cmd.Help()
 		},
 	}
 	e.rootCmd.SetHelpCommand(helpCmd)
 
 	lintersHelpCmd := &cobra.Command{
-		Use:   "linters",
-		Short: "Help about linters",
-		Run:   e.executeLintersHelp,
+		Use:               "linters",
+		Short:             "Help about linters",
+		Args:              cobra.NoArgs,
+		ValidArgsFunction: cobra.NoFileCompletions,
+		Run:               e.executeLintersHelp,
 	}
 	helpCmd.AddCommand(lintersHelpCmd)
 }
@@ -64,11 +60,7 @@ func printLinterConfigs(lcs []*linter.Config) {
 	}
 }
 
-func (e *Executor) executeLintersHelp(_ *cobra.Command, args []string) {
-	if len(args) != 0 {
-		e.log.Fatalf("Usage: golangci-lint help linters")
-	}
-
+func (e *Executor) executeLintersHelp(_ *cobra.Command, _ []string) {
 	var enabledLCs, disabledLCs []*linter.Config
 	for _, lc := range e.DBManager.GetAllSupportedLinterConfigs() {
 		if lc.EnabledByDefault {
@@ -93,6 +85,4 @@ func (e *Executor) executeLintersHelp(_ *cobra.Command, args []string) {
 		sort.Strings(linterNames)
 		fmt.Fprintf(logutils.StdOut, "%s: %s\n", color.YellowString(p), strings.Join(linterNames, ", "))
 	}
-
-	os.Exit(exitcodes.Success)
 }
diff --git a/pkg/commands/linters.go b/pkg/commands/linters.go
index 63328e4e..13bb944d 100644
--- a/pkg/commands/linters.go
+++ b/pkg/commands/linters.go
@@ -1,35 +1,31 @@
 package commands
 
 import (
-	"log"
-	"os"
+	"fmt"
 
 	"github.com/fatih/color"
 	"github.com/spf13/cobra"
 
-	"github.com/golangci/golangci-lint/pkg/exitcodes"
 	"github.com/golangci/golangci-lint/pkg/lint/linter"
 )
 
 func (e *Executor) initLinters() {
 	e.lintersCmd = &cobra.Command{
-		Use:   "linters",
-		Short: "List current linters configuration",
-		Run:   e.executeLinters,
+		Use:               "linters",
+		Short:             "List current linters configuration",
+		Args:              cobra.NoArgs,
+		ValidArgsFunction: cobra.NoFileCompletions,
+		RunE:              e.executeLinters,
 	}
 	e.rootCmd.AddCommand(e.lintersCmd)
 	e.initRunConfiguration(e.lintersCmd)
 }
 
 // executeLinters runs the 'linters' CLI command, which displays the supported linters.
-func (e *Executor) executeLinters(_ *cobra.Command, args []string) {
-	if len(args) != 0 {
-		e.log.Fatalf("Usage: golangci-lint linters")
-	}
-
+func (e *Executor) executeLinters(_ *cobra.Command, _ []string) error {
 	enabledLintersMap, err := e.EnabledLintersSet.GetEnabledLintersMap()
 	if err != nil {
-		log.Fatalf("Can't get enabled linters: %s", err)
+		return fmt.Errorf("can't get enabled linters: %w", err)
 	}
 
 	color.Green("Enabled by your configuration linters:\n")
@@ -49,5 +45,5 @@ func (e *Executor) executeLinters(_ *cobra.Command, args []string) {
 	color.Red("\nDisabled by your configuration linters:\n")
 	printLinterConfigs(disabledLCs)
 
-	os.Exit(exitcodes.Success)
+	return nil
 }
diff --git a/pkg/commands/root.go b/pkg/commands/root.go
index 141fc87f..8ab11960 100644
--- a/pkg/commands/root.go
+++ b/pkg/commands/root.go
@@ -12,14 +12,13 @@ import (
 	"github.com/spf13/pflag"
 
 	"github.com/golangci/golangci-lint/pkg/config"
-	"github.com/golangci/golangci-lint/pkg/exitcodes"
 	"github.com/golangci/golangci-lint/pkg/logutils"
 )
 
-func (e *Executor) persistentPreRun(_ *cobra.Command, _ []string) {
+func (e *Executor) persistentPreRun(_ *cobra.Command, _ []string) error {
 	if e.cfg.Run.PrintVersion {
-		fmt.Fprintf(logutils.StdOut, "golangci-lint has version %s built from %s on %s\n", e.version, e.commit, e.date)
-		os.Exit(exitcodes.Success)
+		_, _ = fmt.Fprintf(logutils.StdOut, "golangci-lint has version %s built from %s on %s\n", e.version, e.commit, e.date)
+		return nil
 	}
 
 	runtime.GOMAXPROCS(e.cfg.Run.Concurrency)
@@ -27,10 +26,10 @@ func (e *Executor) persistentPreRun(_ *cobra.Command, _ []string) {
 	if e.cfg.Run.CPUProfilePath != "" {
 		f, err := os.Create(e.cfg.Run.CPUProfilePath)
 		if err != nil {
-			e.log.Fatalf("Can't create file %s: %s", e.cfg.Run.CPUProfilePath, err)
+			return fmt.Errorf("can't create file %s: %w", e.cfg.Run.CPUProfilePath, err)
 		}
 		if err := pprof.StartCPUProfile(f); err != nil {
-			e.log.Fatalf("Can't start CPU profiling: %s", err)
+			return fmt.Errorf("can't start CPU profiling: %w", err)
 		}
 	}
 
@@ -43,22 +42,25 @@ func (e *Executor) persistentPreRun(_ *cobra.Command, _ []string) {
 	if e.cfg.Run.TracePath != "" {
 		f, err := os.Create(e.cfg.Run.TracePath)
 		if err != nil {
-			e.log.Fatalf("Can't create file %s: %s", e.cfg.Run.TracePath, err)
+			return fmt.Errorf("can't create file %s: %w", e.cfg.Run.TracePath, err)
 		}
 		if err = trace.Start(f); err != nil {
-			e.log.Fatalf("Can't start tracing: %s", err)
+			return fmt.Errorf("can't start tracing: %w", err)
 		}
 	}
+
+	return nil
 }
 
-func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) {
+func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) error {
 	if e.cfg.Run.CPUProfilePath != "" {
 		pprof.StopCPUProfile()
 	}
+
 	if e.cfg.Run.MemProfilePath != "" {
 		f, err := os.Create(e.cfg.Run.MemProfilePath)
 		if err != nil {
-			e.log.Fatalf("Can't create file %s: %s", e.cfg.Run.MemProfilePath, err)
+			return fmt.Errorf("can't create file %s: %w", e.cfg.Run.MemProfilePath, err)
 		}
 
 		var ms runtime.MemStats
@@ -66,15 +68,18 @@ func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) {
 		printMemStats(&ms, e.log)
 
 		if err := pprof.WriteHeapProfile(f); err != nil {
-			e.log.Fatalf("Can't write heap profile: %s", err)
+			return fmt.Errorf("cCan't write heap profile: %w", err)
 		}
-		f.Close()
+		_ = f.Close()
 	}
+
 	if e.cfg.Run.TracePath != "" {
 		trace.Stop()
 	}
 
 	os.Exit(e.exitCode)
+
+	return nil
 }
 
 func printMemStats(ms *runtime.MemStats, logger logutils.Log) {
@@ -120,16 +125,12 @@ func (e *Executor) initRoot() {
 		Use:   "golangci-lint",
 		Short: "golangci-lint is a smart linters runner.",
 		Long:  `Smart, fast linters runner. Run it in cloud for every GitHub pull request on https://golangci.com`,
-		Run: func(cmd *cobra.Command, args []string) {
-			if len(args) != 0 {
-				e.log.Fatalf("Usage: golangci-lint")
-			}
-			if err := cmd.Help(); err != nil {
-				e.log.Fatalf("Can't run help: %s", err)
-			}
+		Args:  cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, _ []string) error {
+			return cmd.Help()
 		},
-		PersistentPreRun:  e.persistentPreRun,
-		PersistentPostRun: e.persistentPostRun,
+		PersistentPreRunE:  e.persistentPreRun,
+		PersistentPostRunE: e.persistentPostRun,
 	}
 
 	initRootFlagSet(rootCmd.PersistentFlags(), e.cfg, e.needVersionOption())
diff --git a/pkg/commands/run.go b/pkg/commands/run.go
index f4716d18..c6b72912 100644
--- a/pkg/commands/run.go
+++ b/pkg/commands/run.go
@@ -279,10 +279,11 @@ func (e *Executor) initRun() {
 		Use:   "run",
 		Short: "Run the linters",
 		Run:   e.executeRun,
-		PreRun: func(_ *cobra.Command, _ []string) {
+		PreRunE: func(_ *cobra.Command, _ []string) error {
 			if ok := e.acquireFileLock(); !ok {
-				e.log.Fatalf("Parallel golangci-lint is running")
+				return fmt.Errorf("parallel golangci-lint is running")
 			}
+			return nil
 		},
 		PostRun: func(_ *cobra.Command, _ []string) {
 			e.releaseFileLock()
diff --git a/pkg/commands/version.go b/pkg/commands/version.go
index 8b48e515..93e4a8ed 100644
--- a/pkg/commands/version.go
+++ b/pkg/commands/version.go
@@ -3,6 +3,7 @@ package commands
 import (
 	"encoding/json"
 	"fmt"
+	"os"
 	"strings"
 
 	"github.com/spf13/cobra"
@@ -31,27 +32,28 @@ func initVersionFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
 
 func (e *Executor) initVersion() {
 	versionCmd := &cobra.Command{
-		Use:   "version",
-		Short: "Version",
+		Use:               "version",
+		Short:             "Version",
+		Args:              cobra.NoArgs,
+		ValidArgsFunction: cobra.NoFileCompletions,
 		RunE: func(cmd *cobra.Command, _ []string) error {
 			switch strings.ToLower(e.cfg.Version.Format) {
 			case "short":
 				fmt.Println(e.version)
+				return nil
+
 			case "json":
 				ver := jsonVersion{
 					Version: e.version,
 					Commit:  e.commit,
 					Date:    e.date,
 				}
-				data, err := json.Marshal(&ver)
-				if err != nil {
-					return err
-				}
-				fmt.Println(string(data))
+				return json.NewEncoder(os.Stdout).Encode(&ver)
+
 			default:
 				fmt.Printf("golangci-lint has version %s built from %s on %s\n", e.version, e.commit, e.date)
+				return nil
 			}
-			return nil
 		},
 	}