diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 40f50cc..01c5b17 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -27,6 +27,9 @@ jobs:
       - name: Check code format
         run: ./gradlew spotlessCheck
       
+      - name: Lint
+        run: make lint
+      
       - name: Test
         run: make test
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index deb0be2..b5bc142 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## Release (2025-xx-xx)
+- `core`: [v0.3.0](core/CHANGELOG.md#v030)
+  - **Feature:** New exception types for better error handling
+    - `AuthenticationException`: New exception for authentication-related failures (token generation, refresh, validation)
+
 ## Release (2025-09-30)
 - `core`: [v0.2.0](core/CHANGELOG.md#v020)
   - **Feature:** Support for passing custom OkHttpClient objects
diff --git a/Makefile b/Makefile
index 945cf42..686a63b 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ fmt:
 	@./gradlew spotlessApply
 
 lint:
-	@echo "linting not ready yet"
+	@./gradlew pmdMain
 
 test:
 	@./gradlew test
diff --git a/build.gradle b/build.gradle
index f4d6b3f..07e2e25 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,6 +3,7 @@ plugins {
 	id 'signing'
 	id 'idea'
 	id 'eclipse'
+	id 'pmd'
 
 	id 'com.diffplug.spotless' version '6.21.0'
 
@@ -13,6 +14,7 @@ plugins {
 
 allprojects {
 	apply plugin: 'com.diffplug.spotless'
+	apply plugin: 'pmd'
 
 	repositories {
 		mavenCentral()
@@ -64,6 +66,17 @@ allprojects {
 			endWithNewline()
 		}
 	}
+
+	pmd {
+		consoleOutput = true
+		toolVersion = "7.12.0"
+
+		// This tells PMD to use your custom ruleset file.
+		ruleSetFiles = rootProject.files("config/pmd/pmd-ruleset.xml")
+
+		// This is important: it prevents PMD from using its default rules.
+		ruleSets = []
+	}
 }
 
 def configureMavenCentralPublishing(Project project) {
diff --git a/config/pmd/pmd-ruleset.xml b/config/pmd/pmd-ruleset.xml
new file mode 100644
index 0000000..7c1187b
--- /dev/null
+++ b/config/pmd/pmd-ruleset.xml
@@ -0,0 +1,68 @@
+
+
+
+    
+        Custom ruleset that excludes generated code from PMD analysis.
+    
+
+    .*/cloud/stackit/sdk/.*/model/.*
+    .*/cloud/stackit/sdk/.*/api/.*
+    .*/cloud/stackit/sdk/.*/ApiCallback.java
+    .*/cloud/stackit/sdk/.*/ApiClient.java
+    .*/cloud/stackit/sdk/.*/ApiResponse.java
+    .*/cloud/stackit/sdk/.*/GzipRequestInterceptor.java
+    .*/cloud/stackit/sdk/.*/JSON.java
+    .*/cloud/stackit/sdk/.*/Pair.java
+    .*/cloud/stackit/sdk/.*/ProgressRequestBody.java
+    .*/cloud/stackit/sdk/.*/ProgressResponseBody.java
+    .*/cloud/stackit/sdk/.*/ServerConfiguration.java
+    .*/cloud/stackit/sdk/.*/ServerVariable.java
+    .*/cloud/stackit/sdk/.*/StringUtil.java
+
+    
+        
+        
+    
+    
+    
+        
+        
+        
+        
+        
+    
+    
+    
+    
+        
+    
+    
+    
+        
+    
+    
+    
+        
+            
+            
+        
+    
+    
+    
+        
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+
+
diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md
index 324370b..36fd0c7 100644
--- a/core/CHANGELOG.md
+++ b/core/CHANGELOG.md
@@ -1,3 +1,7 @@
+## v0.3.0
+- **Feature:** New exception types for better error handling
+  - `AuthenticationException`: New exception for authentication-related failures (token generation, refresh, validation)
+
 ## v0.2.0
 - **Feature:** Support for passing custom OkHttpClient objects
   - `KeyFlowAuthenticator`: Add new constructors with an `OkHttpClientParam`
diff --git a/core/VERSION b/core/VERSION
index 0ea3a94..0d91a54 100644
--- a/core/VERSION
+++ b/core/VERSION
@@ -1 +1 @@
-0.2.0
+0.3.0
diff --git a/core/src/main/java/cloud/stackit/sdk/core/KeyFlowAuthenticator.java b/core/src/main/java/cloud/stackit/sdk/core/KeyFlowAuthenticator.java
index d321ed3..2cb552b 100644
--- a/core/src/main/java/cloud/stackit/sdk/core/KeyFlowAuthenticator.java
+++ b/core/src/main/java/cloud/stackit/sdk/core/KeyFlowAuthenticator.java
@@ -4,6 +4,7 @@
 import cloud.stackit.sdk.core.config.CoreConfiguration;
 import cloud.stackit.sdk.core.config.EnvironmentVariables;
 import cloud.stackit.sdk.core.exception.ApiException;
+import cloud.stackit.sdk.core.exception.AuthenticationException;
 import cloud.stackit.sdk.core.model.ServiceAccountKey;
 import cloud.stackit.sdk.core.utils.Utils;
 import com.auth0.jwt.JWT;
@@ -19,14 +20,16 @@
 import java.security.interfaces.RSAPrivateKey;
 import java.security.spec.InvalidKeySpecException;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 import okhttp3.*;
 import org.jetbrains.annotations.NotNull;
 
-/* KeyFlowAuthenticator handles the Key Flow Authentication based on the Service Account Key. */
+/*
+ * KeyFlowAuthenticator handles the Key Flow Authentication based on the Service Account Key.
+ */
 public class KeyFlowAuthenticator implements Authenticator {
 	private static final String REFRESH_TOKEN = "refresh_token";
 	private static final String ASSERTION = "assertion";
@@ -44,6 +47,8 @@ public class KeyFlowAuthenticator implements Authenticator {
 	private final String tokenUrl;
 	private long tokenLeewayInSeconds = DEFAULT_TOKEN_LEEWAY;
 
+	private final Object tokenRefreshMonitor = new Object();
+
 	/**
 	 * Creates the initial service account and refreshes expired access token.
 	 *
@@ -128,7 +133,7 @@ public Request authenticate(Route route, @NotNull Response response) throws IOEx
 		try {
 			accessToken = getAccessToken();
 		} catch (ApiException | InvalidKeySpecException e) {
-			throw new RuntimeException(e);
+			throw new AuthenticationException("Failed to obtain access token", e);
 		}
 
 		// Return a new request with the refreshed token
@@ -140,19 +145,19 @@ public Request authenticate(Route route, @NotNull Response response) throws IOEx
 
 	protected static class KeyFlowTokenResponse {
 		@SerializedName("access_token")
-		private String accessToken;
+		private final String accessToken;
 
 		@SerializedName("refresh_token")
-		private String refreshToken;
+		private final String refreshToken;
 
 		@SerializedName("expires_in")
 		private long expiresIn;
 
 		@SerializedName("scope")
-		private String scope;
+		private final String scope;
 
 		@SerializedName("token_type")
-		private String tokenType;
+		private final String tokenType;
 
 		public KeyFlowTokenResponse(
 				String accessToken,
@@ -184,14 +189,16 @@ protected String getAccessToken() {
 	 * @throws IOException request for new access token failed
 	 * @throws ApiException response for new access token with bad status code
 	 */
-	public synchronized String getAccessToken()
-			throws IOException, ApiException, InvalidKeySpecException {
-		if (token == null) {
-			createAccessToken();
-		} else if (token.isExpired()) {
-			createAccessTokenWithRefreshToken();
+	@SuppressWarnings("PMD.AvoidSynchronizedStatement")
+	public String getAccessToken() throws IOException, ApiException, InvalidKeySpecException {
+		synchronized (tokenRefreshMonitor) {
+			if (token == null) {
+				createAccessToken();
+			} else if (token.isExpired()) {
+				createAccessTokenWithRefreshToken();
+			}
+			return token.getAccessToken();
 		}
-		return token.getAccessToken();
 	}
 
 	/**
@@ -202,20 +209,23 @@ public synchronized String getAccessToken()
 	 * @throws ApiException response for new access token with bad status code
 	 * @throws JsonSyntaxException parsing of the created access token failed
 	 */
-	protected void createAccessToken()
-			throws InvalidKeySpecException, IOException, JsonSyntaxException, ApiException {
-		String grant = "urn:ietf:params:oauth:grant-type:jwt-bearer";
-		String assertion;
-		try {
-			assertion = generateSelfSignedJWT();
-		} catch (NoSuchAlgorithmException e) {
-			throw new RuntimeException(
-					"could not find required algorithm for jwt signing. This should not happen and should be reported on https://github.com/stackitcloud/stackit-sdk-java/issues",
-					e);
+	@SuppressWarnings("PMD.AvoidSynchronizedStatement")
+	protected void createAccessToken() throws InvalidKeySpecException, IOException, ApiException {
+		synchronized (tokenRefreshMonitor) {
+			String assertion;
+			try {
+				assertion = generateSelfSignedJWT();
+			} catch (NoSuchAlgorithmException e) {
+				throw new AuthenticationException(
+						"could not find required algorithm for jwt signing. This should not happen and should be reported on https://github.com/stackitcloud/stackit-sdk-java/issues",
+						e);
+			}
+
+			String grant = "urn:ietf:params:oauth:grant-type:jwt-bearer";
+			try (Response response = requestToken(grant, assertion).execute()) {
+				parseTokenResponse(response);
+			}
 		}
-		Response response = requestToken(grant, assertion).execute();
-		parseTokenResponse(response);
-		response.close();
 	}
 
 	/**
@@ -225,16 +235,24 @@ protected void createAccessToken()
 	 * @throws ApiException response for new access token with bad status code
 	 * @throws JsonSyntaxException can not parse new access token
 	 */
-	protected synchronized void createAccessTokenWithRefreshToken()
-			throws IOException, JsonSyntaxException, ApiException {
-		String refreshToken = token.refreshToken;
-		Response response = requestToken(REFRESH_TOKEN, refreshToken).execute();
-		parseTokenResponse(response);
-		response.close();
+	@SuppressWarnings("PMD.AvoidSynchronizedStatement")
+	protected void createAccessTokenWithRefreshToken() throws IOException, ApiException {
+		synchronized (tokenRefreshMonitor) {
+			String refreshToken = token.refreshToken;
+			try (Response response = requestToken(REFRESH_TOKEN, refreshToken).execute()) {
+				parseTokenResponse(response);
+			}
+		}
 	}
 
-	private synchronized void parseTokenResponse(Response response)
-			throws ApiException, JsonSyntaxException, IOException {
+	/**
+	 * Parses the token response from the server
+	 *
+	 * @param response HTTP response containing the token
+	 * @throws ApiException if the response has a bad status code
+	 * @throws JsonSyntaxException if the response body cannot be parsed
+	 */
+	private void parseTokenResponse(Response response) throws ApiException {
 		if (response.code() != HttpURLConnection.HTTP_OK) {
 			String body = null;
 			if (response.body() != null) {
@@ -256,10 +274,10 @@ private synchronized void parseTokenResponse(Response response)
 		response.body().close();
 	}
 
-	private Call requestToken(String grant, String assertionValue) throws IOException {
+	private Call requestToken(String grant, String assertionValue) {
 		FormBody.Builder bodyBuilder = new FormBody.Builder();
 		bodyBuilder.addEncoded("grant_type", grant);
-		String assertionKey = grant.equals(REFRESH_TOKEN) ? REFRESH_TOKEN : ASSERTION;
+		String assertionKey = REFRESH_TOKEN.equals(grant) ? REFRESH_TOKEN : ASSERTION;
 		bodyBuilder.addEncoded(assertionKey, assertionValue);
 		FormBody body = bodyBuilder.build();
 
@@ -289,7 +307,7 @@ private String generateSelfSignedJWT()
 		prvKey = saKey.getCredentials().getPrivateKeyParsed();
 		Algorithm algorithm = Algorithm.RSA512(prvKey);
 
-		Map jwtHeader = new HashMap<>();
+		Map jwtHeader = new ConcurrentHashMap<>();
 		jwtHeader.put("kid", saKey.getCredentials().getKid());
 
 		return JWT.create()
diff --git a/core/src/main/java/cloud/stackit/sdk/core/KeyFlowInterceptor.java b/core/src/main/java/cloud/stackit/sdk/core/KeyFlowInterceptor.java
index d310489..37043a1 100644
--- a/core/src/main/java/cloud/stackit/sdk/core/KeyFlowInterceptor.java
+++ b/core/src/main/java/cloud/stackit/sdk/core/KeyFlowInterceptor.java
@@ -1,6 +1,7 @@
 package cloud.stackit.sdk.core;
 
 import cloud.stackit.sdk.core.exception.ApiException;
+import cloud.stackit.sdk.core.exception.AuthenticationException;
 import java.io.IOException;
 import java.security.spec.InvalidKeySpecException;
 import okhttp3.Interceptor;
@@ -37,7 +38,8 @@ public Response intercept(Chain chain) throws IOException {
 		} catch (InvalidKeySpecException | ApiException e) {
 			// try-catch required, because ApiException can not be thrown in the implementation
 			// of Interceptor.intercept(Chain chain)
-			throw new RuntimeException(e);
+			throw new AuthenticationException(
+					"Failed to obtain access token for request authentication", e);
 		}
 
 		Request authenticatedRequest =
diff --git a/core/src/main/java/cloud/stackit/sdk/core/auth/SetupAuth.java b/core/src/main/java/cloud/stackit/sdk/core/auth/SetupAuth.java
index b79c08d..0dec702 100644
--- a/core/src/main/java/cloud/stackit/sdk/core/auth/SetupAuth.java
+++ b/core/src/main/java/cloud/stackit/sdk/core/auth/SetupAuth.java
@@ -6,6 +6,7 @@
 import cloud.stackit.sdk.core.config.EnvironmentVariables;
 import cloud.stackit.sdk.core.exception.CredentialsInFileNotFoundException;
 import cloud.stackit.sdk.core.exception.PrivateKeyNotFoundException;
+import cloud.stackit.sdk.core.model.ServiceAccountCredentials;
 import cloud.stackit.sdk.core.model.ServiceAccountKey;
 import cloud.stackit.sdk.core.utils.Utils;
 import com.google.gson.Gson;
@@ -40,9 +41,12 @@ public class SetupAuth {
 	 *     let it handle the rest. Will be removed in April 2026.
 	 */
 	@Deprecated
+	public SetupAuth() {
+		// deprecated
+	}
+
 	// TODO: constructor of SetupAuth should be private after deprecated constructors/methods are
 	// removed (only static methods should remain)
-	public SetupAuth() {}
 
 	/**
 	 * Set up the KeyFlow Authentication and can be integrated in an OkHttp client, by adding
@@ -61,7 +65,8 @@ public SetupAuth(CoreConfiguration cfg) {
 	}
 
 	/*
-	 * @deprecated Use static methods of SetupAuth instead or just use the KeyFlowAuthenticator and let it handle the rest. Will be removed in April 2026.
+	 * @deprecated Use static methods of SetupAuth instead or just use the KeyFlowAuthenticator
+	 * and let it handle the rest. Will be removed in April 2026.
 	 */
 	@Deprecated
 	public void init() throws IOException {
@@ -70,12 +75,13 @@ public void init() throws IOException {
 	}
 
 	/*
-	 * @deprecated Use static methods of SetupAuth instead or just use the KeyFlowAuthenticator and let it handle the rest. Will be removed in April 2026.
+	 * @deprecated Use static methods of SetupAuth instead or just use the KeyFlowAuthenticator
+	 * and let it handle the rest. Will be removed in April 2026.
 	 */
 	@Deprecated
 	public Interceptor getAuthHandler() {
 		if (authHandler == null) {
-			throw new RuntimeException("init() has to be called first");
+			throw new IllegalStateException("init() has to be called first");
 		}
 		return authHandler;
 	}
@@ -120,13 +126,22 @@ private static String getDefaultCredentialsFilePath() {
 	 *     can be found
 	 * @throws IOException thrown when a file can not be found
 	 */
-	public static ServiceAccountKey setupKeyFlow(CoreConfiguration cfg)
-			throws CredentialsInFileNotFoundException, IOException {
+	public static ServiceAccountKey setupKeyFlow(CoreConfiguration cfg) throws IOException {
 		return setupKeyFlow(cfg, new EnvironmentVariables());
 	}
 
+	/**
+	 * Sets up the KeyFlow Authentication
+	 *
+	 * @param cfg Configuration
+	 * @param env Environment variables
+	 * @return Service account key
+	 * @throws CredentialsInFileNotFoundException thrown when no service account key or private key
+	 *     can be found
+	 * @throws IOException thrown when a file can not be found
+	 */
 	protected static ServiceAccountKey setupKeyFlow(CoreConfiguration cfg, EnvironmentVariables env)
-			throws CredentialsInFileNotFoundException, IOException {
+			throws IOException {
 		// Explicit config in code
 		if (Utils.isStringSet(cfg.getServiceAccountKey())) {
 			ServiceAccountKey saKey = ServiceAccountKey.loadFromJson(cfg.getServiceAccountKey());
@@ -179,14 +194,22 @@ protected static ServiceAccountKey setupKeyFlow(CoreConfiguration cfg, Environme
 		return saKey;
 	}
 
+	/**
+	 * Loads the private key into the service account key
+	 *
+	 * @param cfg Configuration
+	 * @param env Environment variables
+	 * @param saKey Service account key
+	 * @throws PrivateKeyNotFoundException if the private key could not be found
+	 */
 	protected static void loadPrivateKey(
-			CoreConfiguration cfg, EnvironmentVariables env, ServiceAccountKey saKey)
-			throws PrivateKeyNotFoundException {
-		if (!saKey.getCredentials().isPrivateKeySet()) {
+			CoreConfiguration cfg, EnvironmentVariables env, ServiceAccountKey saKey) {
+		ServiceAccountCredentials credentials = saKey.getCredentials();
+		if (!credentials.isPrivateKeySet()) {
 			try {
 				String privateKey = getPrivateKey(cfg, env);
-				saKey.getCredentials().setPrivateKey(privateKey);
-			} catch (Exception e) {
+				credentials.setPrivateKey(privateKey);
+			} catch (CredentialsInFileNotFoundException | IOException e) {
 				throw new PrivateKeyNotFoundException("could not find private key", e);
 			}
 		}
@@ -216,13 +239,14 @@ protected static void loadPrivateKey(
 	 * 
 	 *
 	 * @param cfg
+	 * @param env
 	 * @return found private key
 	 * @throws CredentialsInFileNotFoundException throws if no private key could be found
 	 * @throws IOException throws if the provided path can not be found or the file within the
 	 *     pathKey can not be found
 	 */
 	private static String getPrivateKey(CoreConfiguration cfg, EnvironmentVariables env)
-			throws CredentialsInFileNotFoundException, IOException {
+			throws IOException {
 		// Explicit code config
 		// Get private key
 		if (Utils.isStringSet(cfg.getPrivateKey())) {
@@ -279,8 +303,7 @@ private static String getPrivateKey(CoreConfiguration cfg, EnvironmentVariables
 	 *     pathKey can not be found
 	 */
 	protected static String readValueFromCredentialsFile(
-			String path, String valueKey, String pathKey)
-			throws IOException, CredentialsInFileNotFoundException {
+			String path, String valueKey, String pathKey) throws IOException {
 		// Read credentials file
 		String fileContent =
 				new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
diff --git a/core/src/main/java/cloud/stackit/sdk/core/config/CoreConfiguration.java b/core/src/main/java/cloud/stackit/sdk/core/config/CoreConfiguration.java
index caa5ed3..6debb6d 100644
--- a/core/src/main/java/cloud/stackit/sdk/core/config/CoreConfiguration.java
+++ b/core/src/main/java/cloud/stackit/sdk/core/config/CoreConfiguration.java
@@ -13,8 +13,6 @@ public class CoreConfiguration {
 	private String tokenCustomUrl;
 	private Long tokenExpirationLeeway;
 
-	public CoreConfiguration() {}
-
 	public Map getDefaultHeader() {
 		return defaultHeader;
 	}
diff --git a/core/src/main/java/cloud/stackit/sdk/core/exception/ApiException.java b/core/src/main/java/cloud/stackit/sdk/core/exception/ApiException.java
index d7d6210..26779f3 100644
--- a/core/src/main/java/cloud/stackit/sdk/core/exception/ApiException.java
+++ b/core/src/main/java/cloud/stackit/sdk/core/exception/ApiException.java
@@ -5,14 +5,16 @@
 
 /** ApiException class. */
 public class ApiException extends Exception {
-	private static final long serialVersionUID = 1L;
+	private static final long serialVersionUID = 8115526329759018011L;
 
-	private int code = 0;
-	private Map> responseHeaders = null;
-	private String responseBody = null;
+	private int code;
+	private Map> responseHeaders;
+	private String responseBody;
 
 	/** Constructor for ApiException. */
-	public ApiException() {}
+	public ApiException() {
+		super();
+	}
 
 	/**
 	 * Constructor for ApiException.
@@ -162,6 +164,7 @@ public String getResponseBody() {
 	 *
 	 * @return The exception message
 	 */
+	@Override
 	public String getMessage() {
 		return String.format(
 				"Message: %s%nHTTP response code: %s%nHTTP response body: %s%nHTTP response headers: %s",
diff --git a/core/src/main/java/cloud/stackit/sdk/core/exception/AuthenticationException.java b/core/src/main/java/cloud/stackit/sdk/core/exception/AuthenticationException.java
new file mode 100644
index 0000000..0df6324
--- /dev/null
+++ b/core/src/main/java/cloud/stackit/sdk/core/exception/AuthenticationException.java
@@ -0,0 +1,37 @@
+package cloud.stackit.sdk.core.exception;
+
+/**
+ * Exception thrown when authentication operations fail. This includes token generation, refresh,
+ * and validation failures.
+ */
+public class AuthenticationException extends RuntimeException {
+	private static final long serialVersionUID = -7728708330906023941L;
+
+	/**
+	 * Constructs a new AuthenticationException with the specified detail message.
+	 *
+	 * @param message the detail message
+	 */
+	public AuthenticationException(String message) {
+		super(message);
+	}
+
+	/**
+	 * Constructs a new AuthenticationException with the specified detail message and cause.
+	 *
+	 * @param message the detail message
+	 * @param cause the cause of this exception
+	 */
+	public AuthenticationException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	/**
+	 * Constructs a new AuthenticationException with the specified cause.
+	 *
+	 * @param cause the cause of this exception
+	 */
+	public AuthenticationException(Throwable cause) {
+		super(cause);
+	}
+}
diff --git a/core/src/main/java/cloud/stackit/sdk/core/exception/CredentialsInFileNotFoundException.java b/core/src/main/java/cloud/stackit/sdk/core/exception/CredentialsInFileNotFoundException.java
index 052fc01..7efd80f 100644
--- a/core/src/main/java/cloud/stackit/sdk/core/exception/CredentialsInFileNotFoundException.java
+++ b/core/src/main/java/cloud/stackit/sdk/core/exception/CredentialsInFileNotFoundException.java
@@ -1,6 +1,7 @@
 package cloud.stackit.sdk.core.exception;
 
 public class CredentialsInFileNotFoundException extends RuntimeException {
+	private static final long serialVersionUID = -3290974267932615412L;
 
 	public CredentialsInFileNotFoundException(String msg) {
 		super(msg);
diff --git a/core/src/main/java/cloud/stackit/sdk/core/exception/PrivateKeyNotFoundException.java b/core/src/main/java/cloud/stackit/sdk/core/exception/PrivateKeyNotFoundException.java
index 365ea8e..e31688e 100644
--- a/core/src/main/java/cloud/stackit/sdk/core/exception/PrivateKeyNotFoundException.java
+++ b/core/src/main/java/cloud/stackit/sdk/core/exception/PrivateKeyNotFoundException.java
@@ -1,6 +1,7 @@
 package cloud.stackit.sdk.core.exception;
 
 public class PrivateKeyNotFoundException extends RuntimeException {
+	private static final long serialVersionUID = -81419539524374575L;
 
 	public PrivateKeyNotFoundException(String msg) {
 		super(msg);
diff --git a/core/src/main/java/cloud/stackit/sdk/core/utils/TestUtils.java b/core/src/main/java/cloud/stackit/sdk/core/utils/TestUtils.java
index a027a5b..c4a648c 100644
--- a/core/src/main/java/cloud/stackit/sdk/core/utils/TestUtils.java
+++ b/core/src/main/java/cloud/stackit/sdk/core/utils/TestUtils.java
@@ -1,6 +1,9 @@
 package cloud.stackit.sdk.core.utils;
 
+@SuppressWarnings("PMD.TestClassWithoutTestCases")
 public class TestUtils {
 	public static final String MOCK_SERVICE_ACCOUNT_KEY =
 			"{\"id\":\"id\",\"publicKey\":\"publicKey\",\"createdAt\":\"2025-03-26T15:08:45.915+00:00\",\"keyType\":\"keyType\",\"keyOrigin\":\"keyOrigin\",\"keyAlgorithm\":\"keyAlgo\",\"active\":true,\"validUntil\":\"2025-03-26T15:08:45.915+00:00\",\"credentials\":{\"aud\":\"aud\",\"iss\":\"iss\",\"kid\":\"kid\",\"privateKey\":\"privateKey\",\"sub\":\"sub\"}}\n";
+
+	public static final String MOCK_SERVICE_ACCOUNT_PRIVATE_KEY = "privateKey";
 }
diff --git a/core/src/main/java/cloud/stackit/sdk/core/utils/Utils.java b/core/src/main/java/cloud/stackit/sdk/core/utils/Utils.java
index 5a88097..63e9afe 100644
--- a/core/src/main/java/cloud/stackit/sdk/core/utils/Utils.java
+++ b/core/src/main/java/cloud/stackit/sdk/core/utils/Utils.java
@@ -1,7 +1,31 @@
 package cloud.stackit.sdk.core.utils;
 
 public final class Utils {
-	public static boolean isStringSet(String input) {
-		return input != null && !input.trim().isEmpty();
+	private Utils() {}
+
+	/*
+	 * Assert a string is not null and not empty
+	 *
+	 * @param input The string to check
+	 * @return check result
+	 * */
+	public static boolean isStringSet(final String input) {
+		return input != null && !checkTrimEmpty(input);
+	}
+
+	/*
+	 * Assert a string is not empty. Helper method because String.trim().length() == 0
+	 * / String.trim().isEmpty() is an inefficient way to validate a blank String.
+	 *
+	 * @param input The string to check
+	 * @return check result
+	 * */
+	private static boolean checkTrimEmpty(String input) {
+		for (int i = 0; i < input.length(); i++) {
+			if (!Character.isWhitespace(input.charAt(i))) {
+				return false;
+			}
+		}
+		return true;
 	}
 }
diff --git a/core/src/test/java/cloud/stackit/sdk/core/KeyFlowAuthenticatorTest.java b/core/src/test/java/cloud/stackit/sdk/core/KeyFlowAuthenticatorTest.java
index dac8362..7f763ce 100644
--- a/core/src/test/java/cloud/stackit/sdk/core/KeyFlowAuthenticatorTest.java
+++ b/core/src/test/java/cloud/stackit/sdk/core/KeyFlowAuthenticatorTest.java
@@ -22,12 +22,16 @@
 import okhttp3.mockwebserver.MockWebServer;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
+@SuppressWarnings("PMD.TooManyMethods")
 class KeyFlowAuthenticatorTest {
-	private static MockWebServer mockWebServer;
+	private MockWebServer mockWebServer;
 	private ServiceAccountKey defaultSaKey;
 	private OkHttpClient httpClient;
+
+	private static final String MOCK_WEBSERVER_PATH = "/token";
 	private static final String PRIVATE_KEY =
 			"-----BEGIN PRIVATE KEY-----\n"
 					+ "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0jVPq7ACbkwW6\n"
@@ -58,7 +62,7 @@ class KeyFlowAuthenticatorTest {
 					+ "h/9afEtu5aUE/m+1vGBoH8z1\n"
 					+ "-----END PRIVATE KEY-----\n";
 
-	ServiceAccountKey createDummyServiceAccount() {
+	private ServiceAccountKey createDummyServiceAccount() {
 		ServiceAccountCredentials credentials =
 				new ServiceAccountCredentials("aud", "iss", "kid", PRIVATE_KEY, "sub");
 		return new ServiceAccountKey(
@@ -75,7 +79,7 @@ ServiceAccountKey createDummyServiceAccount() {
 				credentials);
 	}
 
-	KeyFlowAuthenticator.KeyFlowTokenResponse mockResponseBody(boolean expired)
+	private KeyFlowAuthenticator.KeyFlowTokenResponse mockResponseBody(boolean expired)
 			throws NoSuchAlgorithmException, InvalidKeySpecException {
 		Date issuedAt = new Date();
 		Date expiredAt = Date.from(new Date().toInstant().plusSeconds(60 * 10));
@@ -106,7 +110,8 @@ void tearDown() throws IOException {
 	}
 
 	@Test
-	void getAccessToken_response200_noException()
+	@DisplayName("get access token - Response 200 - No exception")
+	void testGetAccessTokenResponse200NoException()
 			throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, ApiException {
 
 		// Setup mockServer
@@ -117,7 +122,7 @@ void getAccessToken_response200_noException()
 		mockWebServer.enqueue(mockedResponse);
 
 		// Config
-		HttpUrl url = mockWebServer.url("/token");
+		HttpUrl url = mockWebServer.url(MOCK_WEBSERVER_PATH);
 		CoreConfiguration cfg =
 				new CoreConfiguration().tokenCustomUrl(url.toString()); // Use mockWebServer
 
@@ -129,7 +134,8 @@ void getAccessToken_response200_noException()
 	}
 
 	@Test
-	void getAccessToken_expiredToken_noException()
+	@DisplayName("get access token - expired token - no exception")
+	void testGetAccessTokenExpiredTokenNoException()
 			throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, ApiException {
 		// Setup expiredToken and newToken
 		KeyFlowAuthenticator.KeyFlowTokenResponse expiredKey = mockResponseBody(true);
@@ -143,7 +149,7 @@ void getAccessToken_expiredToken_noException()
 		mockWebServer.enqueue(mockedResponse);
 
 		// Config
-		HttpUrl url = mockWebServer.url("/token");
+		HttpUrl url = mockWebServer.url(MOCK_WEBSERVER_PATH);
 		CoreConfiguration cfg =
 				new CoreConfiguration().tokenCustomUrl(url.toString()); // Use mockWebServer
 
@@ -155,11 +161,12 @@ void getAccessToken_expiredToken_noException()
 	}
 
 	@Test
-	void createAccessToken_response200WithEmptyBody_throwsException() {
+	@DisplayName("create access token - response 200 with empty body - throws exception")
+	void createAccessTokenResponse200WithEmptyBodyThrowsException() {
 		// Setup mockServer
 		MockResponse mockedResponse = new MockResponse().setResponseCode(200);
 		mockWebServer.enqueue(mockedResponse);
-		HttpUrl url = mockWebServer.url("/token");
+		HttpUrl url = mockWebServer.url(MOCK_WEBSERVER_PATH);
 
 		// Config
 		CoreConfiguration cfg =
@@ -173,11 +180,12 @@ void createAccessToken_response200WithEmptyBody_throwsException() {
 	}
 
 	@Test
-	void createAccessToken_response400_throwsApiException() {
+	@DisplayName("create access token - response 400 - throws ApiException")
+	void createAccessTokenResponse400ThrowsApiException() {
 		// Setup mockServer
 		MockResponse mockedResponse = new MockResponse().setResponseCode(400);
 		mockWebServer.enqueue(mockedResponse);
-		HttpUrl url = mockWebServer.url("/token");
+		HttpUrl url = mockWebServer.url(MOCK_WEBSERVER_PATH);
 
 		// Config
 		CoreConfiguration cfg =
@@ -191,7 +199,8 @@ void createAccessToken_response400_throwsApiException() {
 	}
 
 	@Test
-	void createAccessToken_response200WithValidResponse_noException()
+	@DisplayName("create access token - response 200 with valid response - no exception")
+	void createAccessTokenResponse200WithValidResponseNoException()
 			throws NoSuchAlgorithmException, InvalidKeySpecException {
 		// Setup mockServer
 		KeyFlowAuthenticator.KeyFlowTokenResponse responseBody = mockResponseBody(false);
@@ -201,7 +210,7 @@ void createAccessToken_response200WithValidResponse_noException()
 		mockWebServer.enqueue(mockedResponse);
 
 		// Config
-		HttpUrl url = mockWebServer.url("/token");
+		HttpUrl url = mockWebServer.url(MOCK_WEBSERVER_PATH);
 		CoreConfiguration cfg =
 				new CoreConfiguration().tokenCustomUrl(url.toString()); // Use mockWebServer
 
@@ -213,7 +222,9 @@ void createAccessToken_response200WithValidResponse_noException()
 	}
 
 	@Test
-	void createAccessTokenWithRefreshToken_response200WithValidResponse_noException()
+	@DisplayName(
+			"create access token with refresh token - response 200 with valid response - no exception")
+	void createAccessTokenWithRefreshTokenResponse200WithValidResponseNoException()
 			throws NoSuchAlgorithmException, InvalidKeySpecException {
 		// Setup mockServer
 		KeyFlowAuthenticator.KeyFlowTokenResponse mockedBody = mockResponseBody(false);
@@ -223,7 +234,7 @@ void createAccessTokenWithRefreshToken_response200WithValidResponse_noException(
 		mockWebServer.enqueue(mockedResponse);
 
 		// Config
-		HttpUrl url = mockWebServer.url("/token");
+		HttpUrl url = mockWebServer.url(MOCK_WEBSERVER_PATH);
 		CoreConfiguration cfg =
 				new CoreConfiguration().tokenCustomUrl(url.toString()); // Use mockWebServer
 
@@ -236,13 +247,15 @@ void createAccessTokenWithRefreshToken_response200WithValidResponse_noException(
 	}
 
 	@Test
-	void createAccessTokenWithRefreshToken_response200WithEmptyBody_throwsException()
+	@DisplayName(
+			"create access token with refresh token - response 200 with empty body - throws exception")
+	void createAccessTokenWithRefreshTokenResponse200WithEmptyBodyThrowsException()
 			throws NoSuchAlgorithmException, InvalidKeySpecException {
 		// Setup mockServer
 		KeyFlowAuthenticator.KeyFlowTokenResponse mockResponse = mockResponseBody(false);
 		MockResponse mockedResponse = new MockResponse().setResponseCode(200);
 		mockWebServer.enqueue(mockedResponse);
-		HttpUrl url = mockWebServer.url("/token");
+		HttpUrl url = mockWebServer.url(MOCK_WEBSERVER_PATH);
 
 		// Config
 		CoreConfiguration cfg =
diff --git a/core/src/test/java/cloud/stackit/sdk/core/KeyFlowInterceptorTest.java b/core/src/test/java/cloud/stackit/sdk/core/KeyFlowInterceptorTest.java
index b6f4b39..240ee40 100644
--- a/core/src/test/java/cloud/stackit/sdk/core/KeyFlowInterceptorTest.java
+++ b/core/src/test/java/cloud/stackit/sdk/core/KeyFlowInterceptorTest.java
@@ -13,6 +13,7 @@
 import okhttp3.mockwebserver.RecordedRequest;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
@@ -47,7 +48,8 @@ void teardown() throws IOException {
 	}
 
 	@Test
-	void intercept_addsAuthHeader()
+	@DisplayName("intercept adds auth header")
+	void interceptAddsAuthHeader()
 			throws IOException, InvalidKeySpecException, ApiException, InterruptedException {
 		final String accessToken = "my-access-token";
 		when(authenticator.getAccessToken()).thenReturn(accessToken);
diff --git a/core/src/test/java/cloud/stackit/sdk/core/auth/SetupAuthTest.java b/core/src/test/java/cloud/stackit/sdk/core/auth/SetupAuthTest.java
index 7d6871a..11eebf1 100644
--- a/core/src/test/java/cloud/stackit/sdk/core/auth/SetupAuthTest.java
+++ b/core/src/test/java/cloud/stackit/sdk/core/auth/SetupAuthTest.java
@@ -9,6 +9,7 @@
 import cloud.stackit.sdk.core.exception.PrivateKeyNotFoundException;
 import cloud.stackit.sdk.core.model.ServiceAccountCredentials;
 import cloud.stackit.sdk.core.model.ServiceAccountKey;
+import cloud.stackit.sdk.core.utils.TestUtils;
 import com.google.gson.Gson;
 import java.io.File;
 import java.io.IOException;
@@ -18,16 +19,26 @@
 import java.security.spec.InvalidKeySpecException;
 import java.time.temporal.ChronoUnit;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import javax.swing.filechooser.FileSystemView;
+import okhttp3.Interceptor;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+@SuppressWarnings("PMD.TooManyMethods")
 @ExtendWith(MockitoExtension.class)
 class SetupAuthTest {
+
+	private static final String SERVICE_ACCOUNT_KEY_PREFIX = "serviceAccountKey";
+	private static final String PRIVATE_KEY_PREFIX = "privateKey";
+	private static final String PRIVATE_KEY_VALUE = "prvKey";
+	private static final String PRIVATE_KEY_CONTENT = "";
+	private static final String JSON_FILE_EXTENSION = ".json";
+
 	@Mock private EnvironmentVariables envs;
 	private final String invalidCredentialsFilePath =
 			FileSystemView.getFileSystemView().getHomeDirectory()
@@ -38,7 +49,7 @@ class SetupAuthTest {
 					+ File.separator
 					+ "file.json";
 
-	ServiceAccountKey createDummyServiceAccount(String privateKey) {
+	private ServiceAccountKey createDummyServiceAccount(String privateKey) {
 		ServiceAccountCredentials credentials =
 				new ServiceAccountCredentials("aud", "iss", "kid", privateKey, "sub");
 		return new ServiceAccountKey(
@@ -55,9 +66,9 @@ ServiceAccountKey createDummyServiceAccount(String privateKey) {
 				credentials);
 	}
 
-	Path createJsonFile(Map content) throws IOException {
+	private Path createJsonFile(Map content) throws IOException {
 		String contentJson = new Gson().toJson(content);
-		Path file = Files.createTempFile("credentials", ".json");
+		Path file = Files.createTempFile("credentials", JSON_FILE_EXTENSION);
 		file.toFile().deleteOnExit();
 
 		Files.write(file, contentJson.getBytes(StandardCharsets.UTF_8));
@@ -65,12 +76,34 @@ Path createJsonFile(Map content) throws IOException {
 	}
 
 	@Test
-	void setupKeyFlow_readServiceAccountFromPath()
-			throws IOException, InvalidKeySpecException, ApiException {
+	@DisplayName("get access token - without running init - throws exception")
+	void testGetAccessTokenWithoutRunningInitThrowsException() throws IOException {
+		SetupAuth setupAuth = new SetupAuth();
+		assertThrows(RuntimeException.class, setupAuth::getAuthHandler);
+	}
+
+	@Test
+	@DisplayName("get access token - with running init - returns interceptor")
+	void testGetAccessTokenWithRunningInitReturnsInterceptor() throws IOException {
+		ServiceAccountKey saKey =
+				createDummyServiceAccount(TestUtils.MOCK_SERVICE_ACCOUNT_PRIVATE_KEY);
+		String initSaKeyJson = new Gson().toJson(saKey);
+
+		CoreConfiguration config = new CoreConfiguration().serviceAccountKey(initSaKeyJson);
+
+		SetupAuth setupAuth = new SetupAuth(config);
+		setupAuth.init();
+		assertInstanceOf(Interceptor.class, setupAuth.getAuthHandler());
+	}
+
+	@Test
+	@DisplayName("setup key flow - read service account from path")
+	void setupKeyFlowReadServiceAccountFromPath() throws IOException {
 		// Create service account key file
-		ServiceAccountKey initSaKey = createDummyServiceAccount("privateKey");
+		ServiceAccountKey initSaKey =
+				createDummyServiceAccount(TestUtils.MOCK_SERVICE_ACCOUNT_PRIVATE_KEY);
 		String initSaKeyJson = new Gson().toJson(initSaKey);
-		Path saKeyPath = Files.createTempFile("serviceAccountKey", ".json");
+		Path saKeyPath = Files.createTempFile(SERVICE_ACCOUNT_KEY_PREFIX, JSON_FILE_EXTENSION);
 		saKeyPath.toFile().deleteOnExit();
 		Files.write(saKeyPath, initSaKeyJson.getBytes(StandardCharsets.UTF_8));
 
@@ -84,10 +117,11 @@ void setupKeyFlow_readServiceAccountFromPath()
 	}
 
 	@Test
-	void setupKeyFlow_readServiceAccountFromConfig()
-			throws IOException, InvalidKeySpecException, ApiException {
+	@DisplayName("setup key flow - read service account from config")
+	void setupKeyFlowReadServiceAccountFromConfig() throws IOException {
 		// Create service account key
-		ServiceAccountKey initSaKey = createDummyServiceAccount("privateKey");
+		ServiceAccountKey initSaKey =
+				createDummyServiceAccount(TestUtils.MOCK_SERVICE_ACCOUNT_PRIVATE_KEY);
 		String initSaKeyJson = new Gson().toJson(initSaKey);
 
 		// Create config and read setup auth with the previous created saKey
@@ -98,9 +132,11 @@ void setupKeyFlow_readServiceAccountFromConfig()
 	}
 
 	@Test
-	void setupKeyFlow_readServiceAccountFromKeyEnv() throws IOException {
+	@DisplayName("setup key flow - read service account from key env")
+	void setupKeyFlowReadServiceAccountFromKeyEnv() throws IOException {
 		// Create service account key
-		ServiceAccountKey initSaKey = createDummyServiceAccount("privateKey");
+		ServiceAccountKey initSaKey =
+				createDummyServiceAccount(TestUtils.MOCK_SERVICE_ACCOUNT_PRIVATE_KEY);
 		String initSaKeyJson = new Gson().toJson(initSaKey);
 
 		// Mock env STACKIT_SERVICE_ACCOUNT_KEY
@@ -114,13 +150,14 @@ void setupKeyFlow_readServiceAccountFromKeyEnv() throws IOException {
 	}
 
 	@Test
-	void setupKeyFlow_readServiceAccountFromKeyPathEnv() throws IOException {
+	@DisplayName("setup key flow - read service account from key path env")
+	void setupKeyFlowReadServiceAccountFromKeyPathEnv() throws IOException {
 		// Create service account key
-		ServiceAccountKey initSaKey = createDummyServiceAccount("privateKey");
+		ServiceAccountKey initSaKey = createDummyServiceAccount(PRIVATE_KEY_PREFIX);
 		String keyPathContent = new Gson().toJson(initSaKey);
 
 		// Create dummy keyPathFile
-		Path keyPathFile = Files.createTempFile("serviceAccountKey", ".json");
+		Path keyPathFile = Files.createTempFile(SERVICE_ACCOUNT_KEY_PREFIX, JSON_FILE_EXTENSION);
 		keyPathFile.toFile().deleteOnExit();
 		Files.write(keyPathFile, keyPathContent.getBytes(StandardCharsets.UTF_8));
 
@@ -136,12 +173,14 @@ void setupKeyFlow_readServiceAccountFromKeyPathEnv() throws IOException {
 	}
 
 	@Test
-	void setupKeyFlow_readServiceAccountFromPathWithoutPrivateKey_throwsException()
+	@DisplayName(
+			"setup key flow - read service account from path without private key - throws exception")
+	void setupKeyFlowReadServiceAccountFromPathWithoutPrivateKeyThrowsException()
 			throws IOException, InvalidKeySpecException, ApiException {
 		// Create service account key file
 		ServiceAccountKey initSaKey = createDummyServiceAccount(null);
 		String initSaKeyJson = new Gson().toJson(initSaKey);
-		Path saKeyPath = Files.createTempFile("serviceAccountKey", ".json");
+		Path saKeyPath = Files.createTempFile(SERVICE_ACCOUNT_KEY_PREFIX, JSON_FILE_EXTENSION);
 		saKeyPath.toFile().deleteOnExit();
 		Files.write(saKeyPath, initSaKeyJson.getBytes(StandardCharsets.UTF_8));
 
@@ -157,8 +196,9 @@ void setupKeyFlow_readServiceAccountFromPathWithoutPrivateKey_throwsException()
 	}
 
 	@Test
-	void setupKeyFlow_readServiceAccountFromConfigWithoutPrivateKey_throwsException()
-			throws IOException, InvalidKeySpecException, ApiException {
+	@DisplayName(
+			"setup key flow - read service account from config without private key - throws exception")
+	void setupKeyFlowReadServiceAccountFromConfigWithoutPrivateKeyThrowsException() {
 		// Create service account key
 		ServiceAccountKey initSaKey = createDummyServiceAccount(null);
 		String initSaKeyJson = new Gson().toJson(initSaKey);
@@ -175,9 +215,9 @@ void setupKeyFlow_readServiceAccountFromConfigWithoutPrivateKey_throwsException(
 	}
 
 	@Test
-	void loadPrivateKey_setPrivateKeyFromConfig()
-			throws IOException, InvalidKeySpecException, ApiException {
-		final String prvKey = "prvKey";
+	@DisplayName("load private key - set private key from config")
+	void loadPrivateKeySetPrivateKeyFromConfig() {
+		final String prvKey = PRIVATE_KEY_VALUE;
 		ServiceAccountKey saKey = createDummyServiceAccount(null);
 
 		CoreConfiguration cfg = new CoreConfiguration().privateKey(prvKey);
@@ -188,9 +228,9 @@ void loadPrivateKey_setPrivateKeyFromConfig()
 	}
 
 	@Test
-	void loadPrivateKey_doesNotOverwriteExistingPrivateKey()
-			throws IOException, InvalidKeySpecException, ApiException {
-		final String initialPrivateKey = "prvKey";
+	@DisplayName("load private key - does not overwrite existing private key")
+	void loadPrivateKeyDoesNotOverwriteExistingPrivateKey() {
+		final String initialPrivateKey = PRIVATE_KEY_VALUE;
 		final String cfgPrivateKey = "prvKey-updated";
 
 		// Create Service Account
@@ -203,12 +243,12 @@ void loadPrivateKey_doesNotOverwriteExistingPrivateKey()
 	}
 
 	@Test
-	void loadPrivateKey_setPrivateKeyPath()
-			throws IOException, InvalidKeySpecException, ApiException {
-		Path tempPrvKeyFile = Files.createTempFile("privateKey", ".pem");
+	@DisplayName("load private key - set private key path")
+	void loadPrivateKeySetPrivateKeyPath() throws IOException {
+		Path tempPrvKeyFile = Files.createTempFile(PRIVATE_KEY_PREFIX, ".pem");
 		tempPrvKeyFile.toFile().deleteOnExit();
 
-		final String privateKeyContent = "";
+		final String privateKeyContent = PRIVATE_KEY_CONTENT;
 		Files.write(tempPrvKeyFile, privateKeyContent.getBytes(StandardCharsets.UTF_8));
 
 		// Create Service Account
@@ -222,21 +262,21 @@ void loadPrivateKey_setPrivateKeyPath()
 	}
 
 	@Test
-	void loadPrivateKey_setPrivateKeyPathViaCredentialsFile()
-			throws IOException, InvalidKeySpecException, ApiException {
+	@DisplayName("load private key - set private key path via credentials file")
+	void loadPrivateKeySetPrivateKeyPathViaCredentialsFile() throws IOException {
 		// Create privateKeyFile
-		Path tempPrvKeyFile = Files.createTempFile("privateKey", ".pem");
+		Path tempPrvKeyFile = Files.createTempFile(PRIVATE_KEY_PREFIX, ".pem");
 		tempPrvKeyFile.toFile().deleteOnExit();
 
 		// Write private key file
-		final String privateKeyContent = "";
+		final String privateKeyContent = PRIVATE_KEY_CONTENT;
 		Files.write(tempPrvKeyFile, privateKeyContent.getBytes(StandardCharsets.UTF_8));
 
 		// Create credentialsFile
-		Path tempCredentialsFile = Files.createTempFile("credentialsFile", ".json");
+		Path tempCredentialsFile = Files.createTempFile("credentialsFile", JSON_FILE_EXTENSION);
 		tempCredentialsFile.toFile().deleteOnExit();
 
-		Map credFileContent = new HashMap<>();
+		Map credFileContent = new ConcurrentHashMap<>();
 		credFileContent.put(
 				EnvironmentVariables.ENV_STACKIT_PRIVATE_KEY_PATH,
 				tempPrvKeyFile.toAbsolutePath().toString());
@@ -257,16 +297,16 @@ void loadPrivateKey_setPrivateKeyPathViaCredentialsFile()
 	}
 
 	@Test
-	void loadPrivateKey_setPrivateKeyViaCredentialsFile()
-			throws IOException, InvalidKeySpecException, ApiException {
-		final String privateKeyContent = "";
+	@DisplayName("load private key - set private key via credentials file")
+	void loadPrivateKeySetPrivateKeyViaCredentialsFile() throws IOException {
+		final String privateKeyContent = PRIVATE_KEY_CONTENT;
 
 		// Create credentialsFile
-		Path tempCredentialsFile = Files.createTempFile("credentialsFile", ".json");
+		Path tempCredentialsFile = Files.createTempFile("credentialsFile", JSON_FILE_EXTENSION);
 		tempCredentialsFile.toFile().deleteOnExit();
 
 		// Create dummy credentialsFile
-		Map credFileContent = new HashMap<>();
+		Map credFileContent = new ConcurrentHashMap<>();
 		credFileContent.put(EnvironmentVariables.ENV_STACKIT_PRIVATE_KEY, privateKeyContent);
 		String credFileContentJson = new Gson().toJson(credFileContent);
 
@@ -285,8 +325,9 @@ void loadPrivateKey_setPrivateKeyViaCredentialsFile()
 	}
 
 	@Test
-	void loadPrivateKey_setPrivateKeyViaEnv() throws IOException {
-		final String prvKey = "prvKey";
+	@DisplayName("load private key - set private key via env")
+	void loadPrivateKeySetPrivateKeyViaEnv() {
+		final String prvKey = PRIVATE_KEY_VALUE;
 		ServiceAccountKey saKey = createDummyServiceAccount(null);
 		when(envs.getStackitPrivateKey()).thenReturn(prvKey);
 
@@ -298,10 +339,11 @@ void loadPrivateKey_setPrivateKeyViaEnv() throws IOException {
 	}
 
 	@Test
-	void loadPrivateKey_setPrivateKeyPathViaEnv() throws IOException {
-		final String prvKey = "prvKey";
+	@DisplayName("load private key - set private key path via env")
+	void loadPrivateKeySetPrivateKeyPathViaEnv() throws IOException {
+		final String prvKey = PRIVATE_KEY_VALUE;
 		ServiceAccountKey saKey = createDummyServiceAccount(null);
-		Path tempPrvKeyFile = Files.createTempFile("privateKey", ".pem");
+		Path tempPrvKeyFile = Files.createTempFile(PRIVATE_KEY_PREFIX, ".pem");
 		tempPrvKeyFile.toFile().deleteOnExit();
 		Files.write(tempPrvKeyFile, prvKey.getBytes(StandardCharsets.UTF_8));
 
@@ -316,16 +358,16 @@ void loadPrivateKey_setPrivateKeyPathViaEnv() throws IOException {
 	}
 
 	@Test
-	void loadPrivateKey_setPrivateKeyViaCredentialsFileInEnv()
-			throws IOException, InvalidKeySpecException, ApiException {
-		final String privateKeyContent = "";
+	@DisplayName("load private key - set private key via credentials file in Env")
+	void loadPrivateKeySetPrivateKeyViaCredentialsFileInEnv() throws IOException {
+		final String privateKeyContent = PRIVATE_KEY_CONTENT;
 
 		// Create credentialsFile
-		Path tempCredentialsFile = Files.createTempFile("credentialsFile", ".json");
+		Path tempCredentialsFile = Files.createTempFile("credentialsFile", JSON_FILE_EXTENSION);
 		tempCredentialsFile.toFile().deleteOnExit();
 
 		// Create dummy credentialsFile
-		Map credFileContent = new HashMap<>();
+		Map credFileContent = new ConcurrentHashMap<>();
 		credFileContent.put(EnvironmentVariables.ENV_STACKIT_PRIVATE_KEY, privateKeyContent);
 		String credFileContentJson = new Gson().toJson(credFileContent);
 
@@ -343,7 +385,8 @@ void loadPrivateKey_setPrivateKeyViaCredentialsFileInEnv()
 	}
 
 	@Test
-	void loadPrivateKey_invalidPrivateKeyPath_throwsException()
+	@DisplayName("load private key - invalid private key path - throws exception")
+	void loadPrivateKeyInvalidPrivateKeyPathThrowsException()
 			throws IOException, InvalidKeySpecException, ApiException {
 
 		String invalidPath =
@@ -366,18 +409,18 @@ void loadPrivateKey_invalidPrivateKeyPath_throwsException()
 	}
 
 	@Test
-	void readValueFromCredentialsFile_keyAndKeyPathSet_returnsKeyValue()
-			throws IOException, InvalidKeySpecException, ApiException {
+	@DisplayName("read value from credentials file - key and key path set - returns key value")
+	void readValueFromCredentialsFileKeyAndKeyPathSetReturnsKeyValue() throws IOException {
 		String keyContent = "key";
 		String keyPathContent = "keyPath";
 
 		// Create dummy keyPathFile
-		Path keyPathFile = Files.createTempFile("serviceAccountKey", ".json");
+		Path keyPathFile = Files.createTempFile(SERVICE_ACCOUNT_KEY_PREFIX, JSON_FILE_EXTENSION);
 		keyPathFile.toFile().deleteOnExit();
 		Files.write(keyPathFile, keyPathContent.getBytes(StandardCharsets.UTF_8));
 
 		// Create dummy credentialsFile
-		Map credentialsFileContent = new HashMap<>();
+		Map credentialsFileContent = new ConcurrentHashMap<>();
 		credentialsFileContent.put(
 				EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY, keyContent);
 		credentialsFileContent.put(
@@ -395,12 +438,12 @@ void readValueFromCredentialsFile_keyAndKeyPathSet_returnsKeyValue()
 	}
 
 	@Test
-	void readValueFromCredentialsFile_keySet_returnsKeyValue()
-			throws IOException, InvalidKeySpecException, ApiException {
+	@DisplayName("read value from credentials file - key set - returns key value")
+	void readValueFromCredentialsFileKeySetReturnsKeyValue() throws IOException {
 		String keyContent = "key";
 
 		// Create dummy credentialsFile
-		Map credentialsFileContent = new HashMap<>();
+		Map credentialsFileContent = new ConcurrentHashMap<>();
 		credentialsFileContent.put(
 				EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY, keyContent);
 		Path credentialsFile = createJsonFile(credentialsFileContent);
@@ -415,16 +458,16 @@ void readValueFromCredentialsFile_keySet_returnsKeyValue()
 	}
 
 	@Test
-	void readValueFromCredentialsFile_KeyPathSet_returnsKeyValue()
-			throws IOException, InvalidKeySpecException, ApiException {
+	@DisplayName("read value from credentials file - key path set - returns key value")
+	void readValueFromCredentialsFileKeyPathSetReturnsKeyValue() throws IOException {
 		// Create dummy keyPathFile
 		String keyPathContent = "keyPath";
-		Path keyPathFile = Files.createTempFile("serviceAccountKey", ".json");
+		Path keyPathFile = Files.createTempFile(SERVICE_ACCOUNT_KEY_PREFIX, JSON_FILE_EXTENSION);
 		keyPathFile.toFile().deleteOnExit();
 		Files.write(keyPathFile, keyPathContent.getBytes(StandardCharsets.UTF_8));
 
 		// Create dummy credentialsFile
-		Map credentialsFileContent = new HashMap<>();
+		Map credentialsFileContent = new ConcurrentHashMap<>();
 		credentialsFileContent.put(
 				EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH,
 				keyPathFile.toAbsolutePath().toString());
diff --git a/core/src/test/java/cloud/stackit/sdk/core/config/CoreConfigurationTest.java b/core/src/test/java/cloud/stackit/sdk/core/config/CoreConfigurationTest.java
index d02897a..9a495a4 100644
--- a/core/src/test/java/cloud/stackit/sdk/core/config/CoreConfigurationTest.java
+++ b/core/src/test/java/cloud/stackit/sdk/core/config/CoreConfigurationTest.java
@@ -2,15 +2,16 @@
 
 import static org.junit.jupiter.api.Assertions.*;
 
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import org.junit.jupiter.api.Test;
 
+@SuppressWarnings("PMD.TooManyMethods")
 class CoreConfigurationTest {
 
 	@Test
-	void getDefaultHeader() {
-		HashMap map = new HashMap();
+	void testGetDefaultHeader() {
+		Map map = new ConcurrentHashMap<>();
 		map.put("key", "value");
 		CoreConfiguration cfg = new CoreConfiguration().defaultHeader(map);
 		Map cfgHeader = cfg.getDefaultHeader();
@@ -19,7 +20,7 @@ void getDefaultHeader() {
 	}
 
 	@Test
-	void getServiceAccountKey() {
+	void testGetServiceAccountKey() {
 		final String saKey = "";
 
 		CoreConfiguration cfg = new CoreConfiguration().serviceAccountKey(saKey);
@@ -30,7 +31,7 @@ void getServiceAccountKey() {
 	}
 
 	@Test
-	void getServiceAccountKeyPath() {
+	void testGetServiceAccountKeyPath() {
 		final String saKeyPath = "";
 
 		CoreConfiguration cfg = new CoreConfiguration().serviceAccountKeyPath(saKeyPath);
@@ -41,7 +42,7 @@ void getServiceAccountKeyPath() {
 	}
 
 	@Test
-	void getPrivateKeyPath() {
+	void testGetPrivateKeyPath() {
 		final String privateKeyPath = "";
 
 		CoreConfiguration cfg = new CoreConfiguration().privateKeyPath(privateKeyPath);
@@ -52,7 +53,7 @@ void getPrivateKeyPath() {
 	}
 
 	@Test
-	void getPrivateKey() {
+	void testGetPrivateKey() {
 		final String privateKey = "";
 
 		CoreConfiguration cfg = new CoreConfiguration().privateKey(privateKey);
@@ -63,7 +64,7 @@ void getPrivateKey() {
 	}
 
 	@Test
-	void getCustomEndpoint() {
+	void testGetCustomEndpoint() {
 		final String customEndpoint = "";
 
 		CoreConfiguration cfg = new CoreConfiguration().customEndpoint(customEndpoint);
@@ -74,7 +75,7 @@ void getCustomEndpoint() {
 	}
 
 	@Test
-	void getCredentialsFilePath() {
+	void testGetCredentialsFilePath() {
 		final String credFilePath = "";
 
 		CoreConfiguration cfg = new CoreConfiguration().credentialsFilePath(credFilePath);
@@ -85,7 +86,7 @@ void getCredentialsFilePath() {
 	}
 
 	@Test
-	void getTokenCustomUrl() {
+	void testGetTokenCustomUrl() {
 		final String tokenCustomUrl = "";
 
 		CoreConfiguration cfg = new CoreConfiguration().tokenCustomUrl(tokenCustomUrl);
@@ -96,7 +97,7 @@ void getTokenCustomUrl() {
 	}
 
 	@Test
-	void getTokenExpirationLeeway() {
+	void testGetTokenExpirationLeeway() {
 		final long tokenExpireLeeway = 100;
 
 		CoreConfiguration cfg = new CoreConfiguration().tokenExpirationLeeway(tokenExpireLeeway);
@@ -107,7 +108,7 @@ void getTokenExpirationLeeway() {
 	}
 
 	@Test
-	void getDefaultHeader_not_set() {
+	void testGetDefaultHeaderNotSet() {
 		CoreConfiguration cfg = new CoreConfiguration();
 		Map defaultHeader = cfg.getDefaultHeader();
 
@@ -115,7 +116,7 @@ void getDefaultHeader_not_set() {
 	}
 
 	@Test
-	void getServiceAccountKey_not_set() {
+	void testGetServiceAccountKeyNotSet() {
 		CoreConfiguration cfg = new CoreConfiguration();
 		String serviceAccountKey = cfg.getServiceAccountKey();
 
@@ -123,7 +124,7 @@ void getServiceAccountKey_not_set() {
 	}
 
 	@Test
-	void getServiceAccountKeyPath_not_set() {
+	void testGetServiceAccountKeyPathNotSet() {
 		CoreConfiguration cfg = new CoreConfiguration();
 		String serviceAccountKeyPath = cfg.getServiceAccountKeyPath();
 
@@ -131,7 +132,7 @@ void getServiceAccountKeyPath_not_set() {
 	}
 
 	@Test
-	void getPrivateKeyPath_not_set() {
+	void testGetPrivateKeyPathNotSet() {
 		CoreConfiguration cfg = new CoreConfiguration();
 		String privateKeyPath = cfg.getPrivateKeyPath();
 
@@ -139,7 +140,7 @@ void getPrivateKeyPath_not_set() {
 	}
 
 	@Test
-	void getPrivateKey_not_set() {
+	void testGetPrivateKeyNotSet() {
 		CoreConfiguration cfg = new CoreConfiguration();
 		String privateKey = cfg.getPrivateKey();
 
@@ -147,7 +148,7 @@ void getPrivateKey_not_set() {
 	}
 
 	@Test
-	void getCustomEndpoint_not_set() {
+	void testGetCustomEndpointNotSet() {
 		CoreConfiguration cfg = new CoreConfiguration();
 		String customEndpoint = cfg.getCustomEndpoint();
 
@@ -155,7 +156,7 @@ void getCustomEndpoint_not_set() {
 	}
 
 	@Test
-	void getCredentialsFilePath_not_set() {
+	void testGetCredentialsFilePathNotSet() {
 		CoreConfiguration cfg = new CoreConfiguration();
 		String credentialsFilePath = cfg.getCredentialsFilePath();
 
@@ -163,7 +164,7 @@ void getCredentialsFilePath_not_set() {
 	}
 
 	@Test
-	void getTokenCustomUrl_not_set() {
+	void testGetTokenCustomUrlNotSet() {
 		CoreConfiguration cfg = new CoreConfiguration();
 		String tokenCustomUrl = cfg.getTokenCustomUrl();
 
@@ -171,7 +172,7 @@ void getTokenCustomUrl_not_set() {
 	}
 
 	@Test
-	void getTokenExpirationLeeway_not_set() {
+	void testGetTokenExpirationLeewayNotSet() {
 		CoreConfiguration cfg = new CoreConfiguration();
 		Long tokenExpirationLeeway = cfg.getTokenExpirationLeeway();
 
diff --git a/core/src/test/java/cloud/stackit/sdk/core/utils/UtilsTest.java b/core/src/test/java/cloud/stackit/sdk/core/utils/UtilsTest.java
index 3180081..04effd9 100644
--- a/core/src/test/java/cloud/stackit/sdk/core/utils/UtilsTest.java
+++ b/core/src/test/java/cloud/stackit/sdk/core/utils/UtilsTest.java
@@ -7,36 +7,36 @@
 class UtilsTest {
 
 	@Test
-	void isStringSet_null_returnsFalse() {
+	void isStringSetNull() {
 		assertFalse(Utils.isStringSet(null));
 	}
 
 	@Test
-	void isStringSet_nullString_returnsFalse() {
+	void isStringSetNullString() {
 		String nullString = null;
 		assertFalse(Utils.isStringSet(nullString));
 	}
 
 	@Test
-	void isStringSet_emptyString_returnsFalse() {
+	void isStringSetEmptyString() {
 		String nullString = "";
 		assertFalse(Utils.isStringSet(nullString));
 	}
 
 	@Test
-	void isStringSet_stringWithWhitespaces_returnsFalse() {
+	void isStringSetStringWithWhitespaces() {
 		String nullString = "    ";
 		assertFalse(Utils.isStringSet(nullString));
 	}
 
 	@Test
-	void isStringSet_stringWithText_returnsTrue() {
+	void isStringSetStringWithText() {
 		String nullString = "text";
 		assertTrue(Utils.isStringSet(nullString));
 	}
 
 	@Test
-	void isStringSet_stringWithTextAndWhitespaces_returnsTrue() {
+	void isStringSetStringWithTextAndWhitespaces() {
 		String nullString = " text ";
 		assertTrue(Utils.isStringSet(nullString));
 	}
diff --git a/examples/authentication/src/main/java/cloud/stackit/sdk/authentication/examples/AuthenticationExample.java b/examples/authentication/src/main/java/cloud/stackit/sdk/authentication/examples/AuthenticationExample.java
index 6dbe00f..52f70b0 100644
--- a/examples/authentication/src/main/java/cloud/stackit/sdk/authentication/examples/AuthenticationExample.java
+++ b/examples/authentication/src/main/java/cloud/stackit/sdk/authentication/examples/AuthenticationExample.java
@@ -1,6 +1,7 @@
 package cloud.stackit.sdk.authentication.examples;
 
 import cloud.stackit.sdk.core.config.CoreConfiguration;
+import cloud.stackit.sdk.core.exception.ApiException;
 import cloud.stackit.sdk.resourcemanager.api.ResourceManagerApi;
 import cloud.stackit.sdk.resourcemanager.model.ListOrganizationsResponse;
 import java.io.File;
@@ -8,15 +9,21 @@
 import java.io.IOException;
 import java.util.Scanner;
 
-class AuthenticationExample {
-	public static void main(String[] args) throws IOException {
-		///////////////////////////////////////////////////////
-		// Option 1: setting the paths to service account key (and private key) as configuration
-		///////////////////////////////////////////////////////
-		final String SERVICE_ACCOUNT_KEY_PATH = "/path/to/sa_key.json";
-		final String PRIVATE_KEY_PATH = "/path/to/private_key.pem";
-		final String SERVICE_ACCOUNT_MAIL = "name-1234@sa.stackit.cloud";
+final class AuthenticationExample {
+
+	private static final String SERVICE_ACCOUNT_KEY_PATH = "/path/to/sa_key.json";
+	private static final String PRIVATE_KEY_PATH = "/path/to/private_key.pem";
+	private static final String SERVICE_ACCOUNT_MAIL = "name-1234@sa.stackit.cloud";
+
+	private AuthenticationExample() {}
 
+	@SuppressWarnings({
+		"PMD.CyclomaticComplexity",
+		"PMD.SystemPrintln",
+		"PMD.AvoidThrowingRawExceptionTypes"
+	})
+	public static void main(String[] args) throws IOException {
+		/* OPTION 1: setting the paths to service account key (and private key) as configuration */
 		try {
 			ResourceManagerApi api =
 					new ResourceManagerApi(
@@ -29,17 +36,19 @@ public static void main(String[] args) throws IOException {
 			ListOrganizationsResponse response =
 					api.listOrganizations(null, SERVICE_ACCOUNT_MAIL, null, null, null);
 
-			System.out.println(response);
-		} catch (Exception e) {
+			System.out.println(response.toString());
+		} catch (ApiException | IOException e) {
 			throw new RuntimeException(e);
 		}
 
-		///////////////////////////////////////////////////////
-		// Option 2: setting the service account key (and private key) as configuration
-		///////////////////////////////////////////////////////
+		/*
+		 * OPTION 2: setting the service account key (and private key) as configuration
+		 *
+		 * */
 
-		// read key content from a file, in production you can also read it e.g. from STACKIT
-		// secrets manager, so it's only kept in-memory
+		/* read key content from a file, in production you can also read it
+		 * e.g. from STACKIT secrets manager, so it's only kept in-memory
+		 * */
 		String serviceAccountKeyPath = // replace it with the path to your service account key
 				"examples/authentication/src/main/java/cloud/stackit/sdk/authentication/examples/dummy_credentials/dummy-service-account-key.json";
 		File serviceAccountKeyFile = new File(serviceAccountKeyPath);
@@ -49,7 +58,8 @@ public static void main(String[] args) throws IOException {
 				serviceAccountKeyContent.append(myReader.nextLine());
 			}
 		} catch (FileNotFoundException e) {
-			throw new RuntimeException(e);
+			System.err.println("File not found: " + serviceAccountKeyPath);
+			return;
 		}
 
 		String privateKeyPath = // replace it with the path to your private key
@@ -61,7 +71,8 @@ public static void main(String[] args) throws IOException {
 				privateKeyContent.append(myReader.nextLine());
 			}
 		} catch (FileNotFoundException e) {
-			throw new RuntimeException(e);
+			System.err.println("File not found: " + privateKeyPath);
+			return;
 		}
 
 		String serviceAccountKey = serviceAccountKeyContent.toString();
@@ -79,35 +90,36 @@ public static void main(String[] args) throws IOException {
 			ListOrganizationsResponse response =
 					api.listOrganizations(null, SERVICE_ACCOUNT_MAIL, null, null, null);
 
-			System.out.println(response);
-		} catch (Exception e) {
+			System.out.println(response.toString());
+		} catch (ApiException | IOException e) {
 			throw new RuntimeException(e);
 		}
 
-		///////////////////////////////////////////////////////
-		// Option 3: setting the service account key (and private key) as environment variable
-		///////////////////////////////////////////////////////
-		// Set the service account key via environment variable:
-		// - STACKIT_SERVICE_ACCOUNT_KEY_PATH=/path/to/sa_key.json
-		// - STACKIT_SERVICE_ACCOUNT_KEY=""
-		//
-		// If the private key is not included in the service account key, set also:
-		// - STACKIT_PRIVATE_KEY_PATH=/path/to/private_key.pem
-		// - STACKIT_PRIVATE_KEY=""
-		//
-		// If no environment variable is set, fallback to credentials file in
-		// "$HOME/.stackit/credentials.json".
-		// Can be overridden with the environment variable `STACKIT_CREDENTIALS_PATH`
-		// The credentials file must be a json:
-		//   {
-		//     "STACKIT_SERVICE_ACCOUNT_KEY_PATH": "path/to/sa_key.json",
-		//     "STACKIT_PRIVATE_KEY_PATH": "(OPTIONAL) when the private key isn't included in the
-		// Service Account key",
-		//     // Alternative:
-		//     "STACKIT_SERVICE_ACCOUNT_KEY": "",
-		//     "STACKIT_PRIVATE_KEY": "(OPTIONAL) when the private key isn't included in the Service
-		// Account key",
-		//   }
+		/*
+		 * OPTION 3: setting the service account key (and private key) as environment variable
+		 *
+		 * Set the service account key via environment variable:
+		 * - STACKIT_SERVICE_ACCOUNT_KEY_PATH=/path/to/sa_key.json
+		 * - STACKIT_SERVICE_ACCOUNT_KEY=""
+		 *
+		 * If the private key is not included in the service account key, set also:
+		 * - STACKIT_PRIVATE_KEY_PATH=/path/to/private_key.pem
+		 * - STACKIT_PRIVATE_KEY=""
+		 *
+		 * If no environment variable is set, fallback to credentials file in
+		 * "$HOME/.stackit/credentials.json".
+		 * Can be overridden with the environment variable `STACKIT_CREDENTIALS_PATH`
+		 * The credentials file must be a json:
+		 *   {
+		 *     "STACKIT_SERVICE_ACCOUNT_KEY_PATH": "path/to/sa_key.json",
+		 *     "STACKIT_PRIVATE_KEY_PATH": "(OPTIONAL) when the private key isn't included in the
+		 * Service Account key",
+		 *     // Alternative:
+		 *     "STACKIT_SERVICE_ACCOUNT_KEY": "",
+		 *     "STACKIT_PRIVATE_KEY": "(OPTIONAL) when the private key isn't included in the Service
+		 * Account key",
+		 *   }
+		 * */
 		try {
 			ResourceManagerApi api = new ResourceManagerApi();
 
@@ -115,8 +127,8 @@ public static void main(String[] args) throws IOException {
 			ListOrganizationsResponse response =
 					api.listOrganizations(null, SERVICE_ACCOUNT_MAIL, null, null, null);
 
-			System.out.println(response);
-		} catch (Exception e) {
+			System.out.println(response.toString());
+		} catch (ApiException | IOException e) {
 			throw new RuntimeException(e);
 		}
 	}
diff --git a/examples/custom-http-client/src/main/java/cloud/stackit/sdk/customhttpclient/examples/CustomHttpClientExample.java b/examples/custom-http-client/src/main/java/cloud/stackit/sdk/customhttpclient/examples/CustomHttpClientExample.java
index 7ce4577..e3ba7d5 100644
--- a/examples/custom-http-client/src/main/java/cloud/stackit/sdk/customhttpclient/examples/CustomHttpClientExample.java
+++ b/examples/custom-http-client/src/main/java/cloud/stackit/sdk/customhttpclient/examples/CustomHttpClientExample.java
@@ -14,13 +14,18 @@
  *
  * The example shows how to set the authorization header in the OkHttpClient object (required!).
  *
- * NOTE: Passing the http client is optional, see our other examples where no OkHttpClient object is passed.
+ * NOTE: Passing the http client is optional, see our other examples
+ * where no OkHttpClient object is passed.
  * In this case the STACKIT SDK ApiClients will just create their own OkHttpClient objects.
- * Nevertheless, for production usage try to use one single OkHttpClient object for everything to take advantage of the
- * shared connection pool and to prevent resource leaks.
+ * Nevertheless, for production usage try to use one single OkHttpClient object
+ * for everything to take advantage of the shared connection pool and to prevent resource leaks.
  *
  * */
-public class CustomHttpClientExample {
+final class CustomHttpClientExample {
+
+	private CustomHttpClientExample() {}
+
+	@SuppressWarnings({"PMD.SystemPrintln", "PMD.AvoidThrowingRawExceptionTypes"})
 	public static void main(String[] args) throws IOException {
 		// Credentials are read from the credentialsFile in `~/.stackit/credentials.json` or the env
 		// STACKIT_SERVICE_ACCOUNT_KEY_PATH / STACKIT_SERVICE_ACCOUNT_KEY
diff --git a/examples/iaas/src/main/java/cloud/stackit/sdk/iaas/examples/IaaSExample.java b/examples/iaas/src/main/java/cloud/stackit/sdk/iaas/examples/IaaSExample.java
index d3e1600..accb980 100644
--- a/examples/iaas/src/main/java/cloud/stackit/sdk/iaas/examples/IaaSExample.java
+++ b/examples/iaas/src/main/java/cloud/stackit/sdk/iaas/examples/IaaSExample.java
@@ -4,15 +4,29 @@
 import cloud.stackit.sdk.iaas.api.IaasApi;
 import cloud.stackit.sdk.iaas.model.*;
 import java.io.IOException;
+import java.net.HttpURLConnection;
 import java.util.Collections;
 import java.util.Objects;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
-public class IaaSExample {
+final class IaaSExample {
+
+	private IaaSExample() {}
+
+	@SuppressWarnings({
+		"PMD.CyclomaticComplexity",
+		"PMD.CognitiveComplexity",
+		"PMD.NPathComplexity",
+		"PMD.NcssCount",
+		"PMD.SystemPrintln",
+		"PMD.AvoidThrowingRawExceptionTypes"
+	})
 	public static void main(String[] args) throws IOException {
-		// Credentials are read from the credentialsFile in `~/.stackit/credentials.json` or the env
-		// STACKIT_SERVICE_ACCOUNT_KEY_PATH / STACKIT_SERVICE_ACCOUNT_KEY
+		/*
+		 * Credentials are read from the credentialsFile in `~/.stackit/credentials.json` or the env
+		 * STACKIT_SERVICE_ACCOUNT_KEY_PATH / STACKIT_SERVICE_ACCOUNT_KEY
+		 * */
 		IaasApi iaasApi = new IaasApi();
 
 		// the id of your STACKIT project, read from env var for this example
@@ -24,11 +38,14 @@ public static void main(String[] args) throws IOException {
 		UUID projectId = UUID.fromString(projectIdString);
 
 		try {
-			///////////////////////////////////////////////////////
-			//              N E T W O R K S                      //
-			///////////////////////////////////////////////////////
+			/*
+			 * ///////////////////////////////////////////////////////
+			 * //              N E T W O R K S                      //
+			 * ///////////////////////////////////////////////////////
+			 * */
 
 			/* create a network in the project */
+			@SuppressWarnings("PMD.AvoidUsingHardCodedIP")
 			Network newNetwork =
 					iaasApi.createNetwork(
 							projectId,
@@ -36,7 +53,7 @@ public static void main(String[] args) throws IOException {
 									.name("java-sdk-example-network-01")
 									.dhcp(true)
 									.routed(false)
-									.labels(Collections.singletonMap("foo", "bar"))
+									.labels(Collections.singletonMap("some-network-label", "bar"))
 									.addressFamily(
 											new CreateNetworkAddressFamily()
 													.ipv4(
@@ -50,12 +67,12 @@ public static void main(String[] args) throws IOException {
 					newNetwork.getNetworkId(),
 					new PartialUpdateNetworkPayload()
 							.dhcp(false)
-							.labels(Collections.singletonMap("foo", "bar-updated")));
+							.labels(Collections.singletonMap("some-network-label", "bar-updated")));
 
 			/* fetch the network we just created */
 			Network fetchedNetwork = iaasApi.getNetwork(projectId, newNetwork.getNetworkId());
 			System.out.println("\nFetched network: ");
-			System.out.println("* Name: " + fetchedNetwork.getName());
+			System.out.println("* Network name: " + fetchedNetwork.getName());
 			System.out.println("* Id: " + fetchedNetwork.getNetworkId());
 			System.out.println(
 					"* DHCP: " + (Boolean.TRUE.equals(fetchedNetwork.getDhcp()) ? "YES" : "NO"));
@@ -69,9 +86,11 @@ public static void main(String[] args) throws IOException {
 				System.out.println("* " + network.getName());
 			}
 
-			///////////////////////////////////////////////////////
-			//              I M A G E S                          //
-			///////////////////////////////////////////////////////
+			/*
+			 * ///////////////////////////////////////////////////////
+			 * //              I M A G E S                          //
+			 * ///////////////////////////////////////////////////////
+			 * */
 
 			/* list all available images */
 			ImageListResponse images = iaasApi.listImages(projectId, false, null);
@@ -88,15 +107,17 @@ public static void main(String[] args) throws IOException {
 			assert imageId != null;
 			Image fetchedImage = iaasApi.getImage(projectId, imageId);
 			System.out.println("\nFetched image:");
-			System.out.println("* Name: " + fetchedImage.getName());
-			System.out.println("* Id: " + fetchedImage.getId());
+			System.out.println("* Image name: " + fetchedImage.getName());
+			System.out.println("* Image id: " + fetchedImage.getId());
 			System.out.println("* Checksum: " + fetchedImage.getChecksum());
 			System.out.println("* Created at: " + fetchedImage.getCreatedAt());
 			System.out.println("* Updated at: " + fetchedImage.getUpdatedAt());
 
-			///////////////////////////////////////////////////////
-			//              K E Y P A I R S                      //
-			///////////////////////////////////////////////////////
+			/*
+			 * ///////////////////////////////////////////////////////
+			 * //              K E Y P A I R S                      //
+			 * ///////////////////////////////////////////////////////
+			 * */
 
 			/* list all available keypairs */
 			KeyPairListResponse keypairs = iaasApi.listKeyPairs(null);
@@ -119,7 +140,8 @@ public static void main(String[] args) throws IOException {
 			assert newKeypair.getName() != null;
 			iaasApi.updateKeyPair(
 					newKeypair.getName(),
-					new UpdateKeyPairPayload().labels(Collections.singletonMap("foo", "bar")));
+					new UpdateKeyPairPayload()
+							.labels(Collections.singletonMap("some-keypair-label", "bar")));
 
 			/* fetch the keypair we just created / updated */
 			Keypair fetchedKeypair = iaasApi.getKeyPair(newKeypair.getName());
@@ -131,9 +153,11 @@ public static void main(String[] args) throws IOException {
 			System.out.println("* Fingerprint: " + fetchedKeypair.getFingerprint());
 			System.out.println("* Public key: " + fetchedKeypair.getPublicKey());
 
-			///////////////////////////////////////////////////////
-			//              S E R V E R S                        //
-			///////////////////////////////////////////////////////
+			/*
+			 * ///////////////////////////////////////////////////////
+			 * //              S E R V E R S                        //
+			 * ///////////////////////////////////////////////////////
+			 * */
 
 			/* list all available machine types */
 			MachineTypeListResponse machineTypes = iaasApi.listMachineTypes(projectId, null);
@@ -146,16 +170,20 @@ public static void main(String[] args) throws IOException {
 			MachineType fetchedMachineType =
 					iaasApi.getMachineType(projectId, machineTypes.getItems().get(0).getName());
 			System.out.println("\nFetched machine type: ");
-			System.out.println("* Name: " + fetchedMachineType.getName());
+			System.out.println("* Machine type name: " + fetchedMachineType.getName());
 			System.out.println("* Description: " + fetchedMachineType.getDescription());
 			System.out.println("* Disk size: " + fetchedMachineType.getDisk());
 			System.out.println("* RAM: " + fetchedMachineType.getRam());
 			System.out.println("* vCPUs: " + fetchedMachineType.getVcpus());
 			System.out.println("* Extra specs: " + fetchedMachineType.getExtraSpecs());
 
-			/* create a server */
-			// NOTE: see https://docs.stackit.cloud/stackit/en/virtual-machine-flavors-75137231.html
-			// for available machine types
+			/*
+			 * create a server
+			 *
+			 * NOTE: see the following link for available machine types
+			 * https://docs.stackit.cloud/stackit/en/virtual-machine-flavors-75137231.html
+			 *
+			 * */
 			Server newServer =
 					iaasApi.createServer(
 							projectId,
@@ -231,9 +259,11 @@ public static void main(String[] args) throws IOException {
 			/* reboot the server we just created */
 			iaasApi.rebootServer(projectId, serverId, null);
 
-			///////////////////////////////////////////////////////
-			//              D E L E T I O N                      //
-			///////////////////////////////////////////////////////
+			/*
+			 * ///////////////////////////////////////////////////////
+			 * //              D E L E T I O N                      //
+			 * ///////////////////////////////////////////////////////
+			 * */
 
 			/* delete the server we just created */
 			iaasApi.deleteServer(projectId, serverId);
@@ -246,7 +276,7 @@ public static void main(String[] args) throws IOException {
 					System.out.println("Waiting for server deletion to complete...");
 					TimeUnit.SECONDS.sleep(5);
 				} catch (ApiException e) {
-					if (e.getCode() == 404) {
+					if (e.getCode() == HttpURLConnection.HTTP_NOT_FOUND) {
 						break;
 					}
 				}
diff --git a/examples/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/examples/ResourcemanagerExample.java b/examples/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/examples/ResourcemanagerExample.java
index 34ed2aa..69290b8 100644
--- a/examples/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/examples/ResourcemanagerExample.java
+++ b/examples/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/examples/ResourcemanagerExample.java
@@ -17,7 +17,11 @@
 import java.util.Collections;
 import java.util.UUID;
 
-class ResourcemanagerExample {
+final class ResourcemanagerExample {
+
+	private ResourcemanagerExample() {}
+
+	@SuppressWarnings({"PMD.SystemPrintln", "PMD.AvoidThrowingRawExceptionTypes"})
 	public static void main(String[] args) throws IOException {
 		// Credentials are read from the credentialsFile in `~/.stackit/credentials.json` or the env
 		// STACKIT_SERVICE_ACCOUNT_KEY_PATH / STACKIT_SERVICE_ACCOUNT_KEY
@@ -49,7 +53,9 @@ public static void main(String[] args) throws IOException {
 									.containerParentId(containerParentId.toString())
 									.name("java-test-project")
 									.addMembersItem(member)
-									.labels(Collections.singletonMap("foo", "bar")));
+									.labels(
+											Collections.singletonMap(
+													"some-project-label", "foo-bar")));
 			System.out.println("Project:\n" + project.toString());
 
 			/* list projects */