diff --git a/apps/back/.gitignore b/apps/back/.gitignore
index 7bf5a88..9bbdb61 100644
--- a/apps/back/.gitignore
+++ b/apps/back/.gitignore
@@ -1,3 +1,4 @@
 certs/
 src/db/config.ts
-.env
\ No newline at end of file
+.env
+tsc-out/
\ No newline at end of file
diff --git a/apps/back/package.json b/apps/back/package.json
index bd58dae..ce72c49 100644
--- a/apps/back/package.json
+++ b/apps/back/package.json
@@ -2,18 +2,21 @@
   "name": "@roomruler/back",
   "version": "0.0.0",
   "scripts": {
-    "build": "rollup --config rollup.config.js",
-    "start": "node dist/index.js"
+    "build": "tsc && rollup --config rollup.config.js",
+    "prestart": "pnpm run build",
+    "start": "node dist/index.js",
+    "dev": "rollup --config rollup.config.dev.js --watch"
   },
   "devDependencies": {
     "@rollup/plugin-node-resolve": "^13.0.4",
-    "@rollup/plugin-typescript": "^8.2.5",
+    "@rollup/plugin-run": "^2.1.0",
     "@types/ws": "^7.4.7",
+    "deepmerge": "^4.2.2",
     "rollup": "^2.56.3",
-    "tslib": "^2.3.1",
     "typescript": "^4.4.2"
   },
   "dependencies": {
+    "@roomruler/logger": "workspace:^0.0.0",
     "@roomruler/messages": "workspace:^0.0.0",
     "pg": "^8.7.1",
     "reflect-metadata": "^0.1.13",
diff --git a/apps/back/rollup.config.dev.js b/apps/back/rollup.config.dev.js
new file mode 100644
index 0000000..93e1bd4
--- /dev/null
+++ b/apps/back/rollup.config.dev.js
@@ -0,0 +1,11 @@
+import deepmerge from "deepmerge";
+import run from "@rollup/plugin-run";
+
+import defConfig from "./rollup.config";
+
+export default deepmerge(defConfig, {
+  plugins: [run()],
+  watch: {
+    include: "src/**/*",
+  },
+});
diff --git a/apps/back/rollup.config.js b/apps/back/rollup.config.js
index f43f516..a5cb918 100644
--- a/apps/back/rollup.config.js
+++ b/apps/back/rollup.config.js
@@ -1,11 +1,10 @@
-const typescript = require("@rollup/plugin-typescript");
-const { nodeResolve } = require("@rollup/plugin-node-resolve");
+import { nodeResolve } from "@rollup/plugin-node-resolve";
 
 export default {
-  input: "src/index.ts",
+  input: "tsc-out/index.js",
   output: {
     dir: "dist",
     format: "cjs",
   },
-  plugins: [typescript(), nodeResolve({ resolveOnly: [/^@roomruler\/.*$/] })],
+  plugins: [nodeResolve({ resolveOnly: [/^@roomruler\/.*$/] })],
 };
diff --git a/apps/back/src/index.ts b/apps/back/src/index.ts
index 8c2a14d..1723b66 100644
--- a/apps/back/src/index.ts
+++ b/apps/back/src/index.ts
@@ -7,6 +7,7 @@ import {
   isUpdateMessage,
   ListMessage,
 } from "@roomruler/messages";
+import { logger as log } from "@roomruler/logger";
 
 const main = async () => {
   const connection = await connect();
@@ -15,11 +16,11 @@ const main = async () => {
     {
       port: Number.parseInt(process.env.PORT || "") || 8081,
     },
-    () => console.log(`Started server on ${process.env.PORT || 8081}`)
+    () => log(`Started server on ${process.env.PORT || 8081}`)
   );
 
   wss.on("connection", async (wsc, req) => {
-    console.log("New user connected from " + req.socket.remoteAddress);
+    log("New user connected from " + req.socket.remoteAddress);
     wsc.send(
       JSON.stringify(
         composeMessage<ListMessage>("list", await getRoomList(connection))
@@ -27,14 +28,15 @@ const main = async () => {
     );
 
     wsc.on("message", async (data) => {
-      console.log("Got message from " + req.socket.remoteAddress);
+      log("Got message from " + req.socket.remoteAddress);
       try {
         const message: unknown = JSON.parse(data.toString());
         if (!isMessage(message)) throw new Error("Message corrupted");
 
         if (isUpdateMessage(message)) {
-          console.log(
-            `Processing message of \"${message.type}\" type from ${req.socket.remoteAddress}`
+          log(
+            `\nProcessing message of \"${message.type}\" type from ${req.socket.remoteAddress}\n`,
+            message.args
           );
 
           const { id, value } = message.args;
@@ -46,7 +48,7 @@ const main = async () => {
           });
         }
       } catch (err) {
-        console.log("Error processing message", err);
+        log("Error processing message", err);
       }
     });
   });
diff --git a/apps/back/tsconfig.json b/apps/back/tsconfig.json
index 93205f0..546e3eb 100644
--- a/apps/back/tsconfig.json
+++ b/apps/back/tsconfig.json
@@ -2,8 +2,8 @@
   "extends": "../../tsconfig.json",
   "compilerOptions": {
     "target": "es6",
-    "module": "commonjs",
-    "outDir": "dist"
+    "module": "esnext",
+    "outDir": "tsc-out"
   },
   "include": ["src"],
   "references": [{ "path": "../../packages/messages/tsconfig.json" }]
diff --git a/apps/front/package.json b/apps/front/package.json
index 311446e..0573444 100644
--- a/apps/front/package.json
+++ b/apps/front/package.json
@@ -3,11 +3,13 @@
   "version": "0.0.0",
   "scripts": {
     "dev": "vite",
-    "start": "vite",
+    "start": "serve -s dist",
+    "prestart": "pnpm run build",
     "build": "vite build"
   },
   "devDependencies": {
     "@types/react-dom": "^17.0.9",
+    "serve": "^12.0.0",
     "typescript": "^4.4.2",
     "vite": "^2.5.3",
     "vite-plugin-svgr": "^0.4.0"
diff --git a/package.json b/package.json
index a69b447..4901592 100644
--- a/package.json
+++ b/package.json
@@ -3,8 +3,11 @@
   "version": "1.0.0",
   "description": "Web application for distribution of free classrooms",
   "scripts": {
+    "start": "pnpm start -r",
+    "build": "pnpm run build -r",
+    "dev": "pnpm run dev -r",
     "preinstall": "npx -y only-allow pnpm",
-    "clean": "rm -rf node_modules apps/back/node_modules apps/front/node_modules packages/messages/node_modules apps/back/dist apps/front/dist packages/messages/dist pnpm-lock.yaml"
+    "clean": "rm -rf node_modules apps/back/node_modules apps/front/node_modules packages/messages/node_modules packages/logger/node_modules apps/back/dist apps/front/dist packages/messages/dist packages/logger/dist pnpm-lock.yaml"
   },
   "author": "dm1sh",
   "license": "MIT",
diff --git a/packages/logger/package.json b/packages/logger/package.json
new file mode 100644
index 0000000..81431e2
--- /dev/null
+++ b/packages/logger/package.json
@@ -0,0 +1,15 @@
+{
+  "name": "@roomruler/logger",
+  "main": "dist/src/index.js",
+  "version": "0.0.0",
+  "types": "dist/src/index.d.ts",
+  "type": "module",
+  "scripts": {
+    "postinstall": "pnpm run build",
+    "build": "tsc --build"
+  },
+  "devDependencies": {
+    "typescript": "^4.4.2"
+  },
+  "private": "true"
+}
diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts
new file mode 100644
index 0000000..2cac180
--- /dev/null
+++ b/packages/logger/src/index.ts
@@ -0,0 +1,2 @@
+export const logger = (...args: unknown[]) =>
+  console.log(new Date().toISOString() + ":", ...args);
diff --git a/packages/logger/tsconfig.json b/packages/logger/tsconfig.json
new file mode 100644
index 0000000..3d87a68
--- /dev/null
+++ b/packages/logger/tsconfig.json
@@ -0,0 +1,10 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "module": "es6",
+    "target": "es6",
+    "outDir": "dist",
+    "composite": true
+  },
+  "include": ["src"],
+}
diff --git a/packages/messages/package.json b/packages/messages/package.json
index 35f4d64..33afed9 100644
--- a/packages/messages/package.json
+++ b/packages/messages/package.json
@@ -5,7 +5,8 @@
   "types": "dist/src/index.d.ts",
   "type": "module",
   "scripts": {
-    "build": "tsc --build"
+    "build": "tsc --build",
+    "postinstall": "pnpm run build"
   },
   "devDependencies": {
     "typescript": "^4.4.2"
diff --git a/tsconfig.json b/tsconfig.json
index d60e526..c22c40c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,7 +6,6 @@
     "noLib": false,
     "emitDecoratorMetadata": true,
     "experimentalDecorators": true,
-    "sourceMap": true,
     "strict": true,
     "moduleResolution": "node",
     "resolveJsonModule": true,